| package libtrust |
| |
| import ( |
| "crypto" |
| "crypto/ecdsa" |
| "crypto/elliptic" |
| "crypto/rand" |
| "crypto/x509" |
| "encoding/json" |
| "encoding/pem" |
| "errors" |
| "fmt" |
| "io" |
| "math/big" |
| ) |
| |
| /* |
| * EC DSA PUBLIC KEY |
| */ |
| |
| // ecPublicKey implements a libtrust.PublicKey using elliptic curve digital |
| // signature algorithms. |
| type ecPublicKey struct { |
| *ecdsa.PublicKey |
| curveName string |
| signatureAlgorithm *signatureAlgorithm |
| extended map[string]interface{} |
| } |
| |
| func fromECPublicKey(cryptoPublicKey *ecdsa.PublicKey) (*ecPublicKey, error) { |
| curve := cryptoPublicKey.Curve |
| |
| switch { |
| case curve == elliptic.P256(): |
| return &ecPublicKey{cryptoPublicKey, "P-256", es256, map[string]interface{}{}}, nil |
| case curve == elliptic.P384(): |
| return &ecPublicKey{cryptoPublicKey, "P-384", es384, map[string]interface{}{}}, nil |
| case curve == elliptic.P521(): |
| return &ecPublicKey{cryptoPublicKey, "P-521", es512, map[string]interface{}{}}, nil |
| default: |
| return nil, errors.New("unsupported elliptic curve") |
| } |
| } |
| |
| // KeyType returns the key type for elliptic curve keys, i.e., "EC". |
| func (k *ecPublicKey) KeyType() string { |
| return "EC" |
| } |
| |
| // CurveName returns the elliptic curve identifier. |
| // Possible values are "P-256", "P-384", and "P-521". |
| func (k *ecPublicKey) CurveName() string { |
| return k.curveName |
| } |
| |
| // KeyID returns a distinct identifier which is unique to this Public Key. |
| func (k *ecPublicKey) KeyID() string { |
| return keyIDFromCryptoKey(k) |
| } |
| |
| func (k *ecPublicKey) String() string { |
| return fmt.Sprintf("EC Public Key <%s>", k.KeyID()) |
| } |
| |
| // Verify verifyies the signature of the data in the io.Reader using this |
| // PublicKey. The alg parameter should identify the digital signature |
| // algorithm which was used to produce the signature and should be supported |
| // by this public key. Returns a nil error if the signature is valid. |
| func (k *ecPublicKey) Verify(data io.Reader, alg string, signature []byte) error { |
| // For EC keys there is only one supported signature algorithm depending |
| // on the curve parameters. |
| if k.signatureAlgorithm.HeaderParam() != alg { |
| return fmt.Errorf("unable to verify signature: EC Public Key with curve %q does not support signature algorithm %q", k.curveName, alg) |
| } |
| |
| // signature is the concatenation of (r, s), base64Url encoded. |
| sigLength := len(signature) |
| expectedOctetLength := 2 * ((k.Params().BitSize + 7) >> 3) |
| if sigLength != expectedOctetLength { |
| return fmt.Errorf("signature length is %d octets long, should be %d", sigLength, expectedOctetLength) |
| } |
| |
| rBytes, sBytes := signature[:sigLength/2], signature[sigLength/2:] |
| r := new(big.Int).SetBytes(rBytes) |
| s := new(big.Int).SetBytes(sBytes) |
| |
| hasher := k.signatureAlgorithm.HashID().New() |
| _, err := io.Copy(hasher, data) |
| if err != nil { |
| return fmt.Errorf("error reading data to sign: %s", err) |
| } |
| hash := hasher.Sum(nil) |
| |
| if !ecdsa.Verify(k.PublicKey, hash, r, s) { |
| return errors.New("invalid signature") |
| } |
| |
| return nil |
| } |
| |
| // CryptoPublicKey returns the internal object which can be used as a |
| // crypto.PublicKey for use with other standard library operations. The type |
| // is either *rsa.PublicKey or *ecdsa.PublicKey |
| func (k *ecPublicKey) CryptoPublicKey() crypto.PublicKey { |
| return k.PublicKey |
| } |
| |
| func (k *ecPublicKey) toMap() map[string]interface{} { |
| jwk := make(map[string]interface{}) |
| for k, v := range k.extended { |
| jwk[k] = v |
| } |
| jwk["kty"] = k.KeyType() |
| jwk["kid"] = k.KeyID() |
| jwk["crv"] = k.CurveName() |
| |
| xBytes := k.X.Bytes() |
| yBytes := k.Y.Bytes() |
| octetLength := (k.Params().BitSize + 7) >> 3 |
| // MUST include leading zeros in the output so that x, y are each |
| // *octetLength* bytes long. |
| xBuf := make([]byte, octetLength-len(xBytes), octetLength) |
| yBuf := make([]byte, octetLength-len(yBytes), octetLength) |
| xBuf = append(xBuf, xBytes...) |
| yBuf = append(yBuf, yBytes...) |
| |
| jwk["x"] = joseBase64UrlEncode(xBuf) |
| jwk["y"] = joseBase64UrlEncode(yBuf) |
| |
| return jwk |
| } |
| |
| // MarshalJSON serializes this Public Key using the JWK JSON serialization format for |
| // elliptic curve keys. |
| func (k *ecPublicKey) MarshalJSON() (data []byte, err error) { |
| return json.Marshal(k.toMap()) |
| } |
| |
| // PEMBlock serializes this Public Key to DER-encoded PKIX format. |
| func (k *ecPublicKey) PEMBlock() (*pem.Block, error) { |
| derBytes, err := x509.MarshalPKIXPublicKey(k.PublicKey) |
| if err != nil { |
| return nil, fmt.Errorf("unable to serialize EC PublicKey to DER-encoded PKIX format: %s", err) |
| } |
| k.extended["kid"] = k.KeyID() // For display purposes. |
| return createPemBlock("PUBLIC KEY", derBytes, k.extended) |
| } |
| |
| func (k *ecPublicKey) AddExtendedField(field string, value interface{}) { |
| k.extended[field] = value |
| } |
| |
| func (k *ecPublicKey) GetExtendedField(field string) interface{} { |
| v, ok := k.extended[field] |
| if !ok { |
| return nil |
| } |
| return v |
| } |
| |
| func ecPublicKeyFromMap(jwk map[string]interface{}) (*ecPublicKey, error) { |
| // JWK key type (kty) has already been determined to be "EC". |
| // Need to extract 'crv', 'x', 'y', and 'kid' and check for |
| // consistency. |
| |
| // Get the curve identifier value. |
| crv, err := stringFromMap(jwk, "crv") |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Public Key curve identifier: %s", err) |
| } |
| |
| var ( |
| curve elliptic.Curve |
| sigAlg *signatureAlgorithm |
| ) |
| |
| switch { |
| case crv == "P-256": |
| curve = elliptic.P256() |
| sigAlg = es256 |
| case crv == "P-384": |
| curve = elliptic.P384() |
| sigAlg = es384 |
| case crv == "P-521": |
| curve = elliptic.P521() |
| sigAlg = es512 |
| default: |
| return nil, fmt.Errorf("JWK EC Public Key curve identifier not supported: %q\n", crv) |
| } |
| |
| // Get the X and Y coordinates for the public key point. |
| xB64Url, err := stringFromMap(jwk, "x") |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Public Key x-coordinate: %s", err) |
| } |
| x, err := parseECCoordinate(xB64Url, curve) |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Public Key x-coordinate: %s", err) |
| } |
| |
| yB64Url, err := stringFromMap(jwk, "y") |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Public Key y-coordinate: %s", err) |
| } |
| y, err := parseECCoordinate(yB64Url, curve) |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Public Key y-coordinate: %s", err) |
| } |
| |
| key := &ecPublicKey{ |
| PublicKey: &ecdsa.PublicKey{Curve: curve, X: x, Y: y}, |
| curveName: crv, signatureAlgorithm: sigAlg, |
| } |
| |
| // Key ID is optional too, but if it exists, it should match the key. |
| _, ok := jwk["kid"] |
| if ok { |
| kid, err := stringFromMap(jwk, "kid") |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Public Key ID: %s", err) |
| } |
| if kid != key.KeyID() { |
| return nil, fmt.Errorf("JWK EC Public Key ID does not match: %s", kid) |
| } |
| } |
| |
| key.extended = jwk |
| |
| return key, nil |
| } |
| |
| /* |
| * EC DSA PRIVATE KEY |
| */ |
| |
| // ecPrivateKey implements a JWK Private Key using elliptic curve digital signature |
| // algorithms. |
| type ecPrivateKey struct { |
| ecPublicKey |
| *ecdsa.PrivateKey |
| } |
| |
| func fromECPrivateKey(cryptoPrivateKey *ecdsa.PrivateKey) (*ecPrivateKey, error) { |
| publicKey, err := fromECPublicKey(&cryptoPrivateKey.PublicKey) |
| if err != nil { |
| return nil, err |
| } |
| |
| return &ecPrivateKey{*publicKey, cryptoPrivateKey}, nil |
| } |
| |
| // PublicKey returns the Public Key data associated with this Private Key. |
| func (k *ecPrivateKey) PublicKey() PublicKey { |
| return &k.ecPublicKey |
| } |
| |
| func (k *ecPrivateKey) String() string { |
| return fmt.Sprintf("EC Private Key <%s>", k.KeyID()) |
| } |
| |
| // Sign signs the data read from the io.Reader using a signature algorithm supported |
| // by the elliptic curve private key. If the specified hashing algorithm is |
| // supported by this key, that hash function is used to generate the signature |
| // otherwise the the default hashing algorithm for this key is used. Returns |
| // the signature and the name of the JWK signature algorithm used, e.g., |
| // "ES256", "ES384", "ES512". |
| func (k *ecPrivateKey) Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) { |
| // Generate a signature of the data using the internal alg. |
| // The given hashId is only a suggestion, and since EC keys only support |
| // on signature/hash algorithm given the curve name, we disregard it for |
| // the elliptic curve JWK signature implementation. |
| hasher := k.signatureAlgorithm.HashID().New() |
| _, err = io.Copy(hasher, data) |
| if err != nil { |
| return nil, "", fmt.Errorf("error reading data to sign: %s", err) |
| } |
| hash := hasher.Sum(nil) |
| |
| r, s, err := ecdsa.Sign(rand.Reader, k.PrivateKey, hash) |
| if err != nil { |
| return nil, "", fmt.Errorf("error producing signature: %s", err) |
| } |
| rBytes, sBytes := r.Bytes(), s.Bytes() |
| octetLength := (k.ecPublicKey.Params().BitSize + 7) >> 3 |
| // MUST include leading zeros in the output |
| rBuf := make([]byte, octetLength-len(rBytes), octetLength) |
| sBuf := make([]byte, octetLength-len(sBytes), octetLength) |
| |
| rBuf = append(rBuf, rBytes...) |
| sBuf = append(sBuf, sBytes...) |
| |
| signature = append(rBuf, sBuf...) |
| alg = k.signatureAlgorithm.HeaderParam() |
| |
| return |
| } |
| |
| // CryptoPrivateKey returns the internal object which can be used as a |
| // crypto.PublicKey for use with other standard library operations. The type |
| // is either *rsa.PublicKey or *ecdsa.PublicKey |
| func (k *ecPrivateKey) CryptoPrivateKey() crypto.PrivateKey { |
| return k.PrivateKey |
| } |
| |
| func (k *ecPrivateKey) toMap() map[string]interface{} { |
| jwk := k.ecPublicKey.toMap() |
| |
| dBytes := k.D.Bytes() |
| // The length of this octet string MUST be ceiling(log-base-2(n)/8) |
| // octets (where n is the order of the curve). This is because the private |
| // key d must be in the interval [1, n-1] so the bitlength of d should be |
| // no larger than the bitlength of n-1. The easiest way to find the octet |
| // length is to take bitlength(n-1), add 7 to force a carry, and shift this |
| // bit sequence right by 3, which is essentially dividing by 8 and adding |
| // 1 if there is any remainder. Thus, the private key value d should be |
| // output to (bitlength(n-1)+7)>>3 octets. |
| n := k.ecPublicKey.Params().N |
| octetLength := (new(big.Int).Sub(n, big.NewInt(1)).BitLen() + 7) >> 3 |
| // Create a buffer with the necessary zero-padding. |
| dBuf := make([]byte, octetLength-len(dBytes), octetLength) |
| dBuf = append(dBuf, dBytes...) |
| |
| jwk["d"] = joseBase64UrlEncode(dBuf) |
| |
| return jwk |
| } |
| |
| // MarshalJSON serializes this Private Key using the JWK JSON serialization format for |
| // elliptic curve keys. |
| func (k *ecPrivateKey) MarshalJSON() (data []byte, err error) { |
| return json.Marshal(k.toMap()) |
| } |
| |
| // PEMBlock serializes this Private Key to DER-encoded PKIX format. |
| func (k *ecPrivateKey) PEMBlock() (*pem.Block, error) { |
| derBytes, err := x509.MarshalECPrivateKey(k.PrivateKey) |
| if err != nil { |
| return nil, fmt.Errorf("unable to serialize EC PrivateKey to DER-encoded PKIX format: %s", err) |
| } |
| k.extended["keyID"] = k.KeyID() // For display purposes. |
| return createPemBlock("EC PRIVATE KEY", derBytes, k.extended) |
| } |
| |
| func ecPrivateKeyFromMap(jwk map[string]interface{}) (*ecPrivateKey, error) { |
| dB64Url, err := stringFromMap(jwk, "d") |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Private Key: %s", err) |
| } |
| |
| // JWK key type (kty) has already been determined to be "EC". |
| // Need to extract the public key information, then extract the private |
| // key value 'd'. |
| publicKey, err := ecPublicKeyFromMap(jwk) |
| if err != nil { |
| return nil, err |
| } |
| |
| d, err := parseECPrivateParam(dB64Url, publicKey.Curve) |
| if err != nil { |
| return nil, fmt.Errorf("JWK EC Private Key d-param: %s", err) |
| } |
| |
| key := &ecPrivateKey{ |
| ecPublicKey: *publicKey, |
| PrivateKey: &ecdsa.PrivateKey{ |
| PublicKey: *publicKey.PublicKey, |
| D: d, |
| }, |
| } |
| |
| return key, nil |
| } |
| |
| /* |
| * Key Generation Functions. |
| */ |
| |
| func generateECPrivateKey(curve elliptic.Curve) (k *ecPrivateKey, err error) { |
| k = new(ecPrivateKey) |
| k.PrivateKey, err = ecdsa.GenerateKey(curve, rand.Reader) |
| if err != nil { |
| return nil, err |
| } |
| |
| k.ecPublicKey.PublicKey = &k.PrivateKey.PublicKey |
| k.extended = make(map[string]interface{}) |
| |
| return |
| } |
| |
| // GenerateECP256PrivateKey generates a key pair using elliptic curve P-256. |
| func GenerateECP256PrivateKey() (PrivateKey, error) { |
| k, err := generateECPrivateKey(elliptic.P256()) |
| if err != nil { |
| return nil, fmt.Errorf("error generating EC P-256 key: %s", err) |
| } |
| |
| k.curveName = "P-256" |
| k.signatureAlgorithm = es256 |
| |
| return k, nil |
| } |
| |
| // GenerateECP384PrivateKey generates a key pair using elliptic curve P-384. |
| func GenerateECP384PrivateKey() (PrivateKey, error) { |
| k, err := generateECPrivateKey(elliptic.P384()) |
| if err != nil { |
| return nil, fmt.Errorf("error generating EC P-384 key: %s", err) |
| } |
| |
| k.curveName = "P-384" |
| k.signatureAlgorithm = es384 |
| |
| return k, nil |
| } |
| |
| // GenerateECP521PrivateKey generates aß key pair using elliptic curve P-521. |
| func GenerateECP521PrivateKey() (PrivateKey, error) { |
| k, err := generateECPrivateKey(elliptic.P521()) |
| if err != nil { |
| return nil, fmt.Errorf("error generating EC P-521 key: %s", err) |
| } |
| |
| k.curveName = "P-521" |
| k.signatureAlgorithm = es512 |
| |
| return k, nil |
| } |