forked from TrueCloudLab/distribution
77e69b9cf3
Signed-off-by: Olivier Gambier <olivier@docker.com>
427 lines
13 KiB
Go
427 lines
13 KiB
Go
package libtrust
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
)
|
|
|
|
/*
|
|
* RSA DSA PUBLIC KEY
|
|
*/
|
|
|
|
// rsaPublicKey implements a JWK Public Key using RSA digital signature algorithms.
|
|
type rsaPublicKey struct {
|
|
*rsa.PublicKey
|
|
extended map[string]interface{}
|
|
}
|
|
|
|
func fromRSAPublicKey(cryptoPublicKey *rsa.PublicKey) *rsaPublicKey {
|
|
return &rsaPublicKey{cryptoPublicKey, map[string]interface{}{}}
|
|
}
|
|
|
|
// KeyType returns the JWK key type for RSA keys, i.e., "RSA".
|
|
func (k *rsaPublicKey) KeyType() string {
|
|
return "RSA"
|
|
}
|
|
|
|
// KeyID returns a distinct identifier which is unique to this Public Key.
|
|
func (k *rsaPublicKey) KeyID() string {
|
|
return keyIDFromCryptoKey(k)
|
|
}
|
|
|
|
func (k *rsaPublicKey) String() string {
|
|
return fmt.Sprintf("RSA Public Key <%s>", k.KeyID())
|
|
}
|
|
|
|
// Verify verifyies the signature of the data in the io.Reader using this Public Key.
|
|
// The alg parameter should be the name of the JWA 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 *rsaPublicKey) Verify(data io.Reader, alg string, signature []byte) error {
|
|
// Verify the signature of the given date, return non-nil error if valid.
|
|
sigAlg, err := rsaSignatureAlgorithmByName(alg)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to verify Signature: %s", err)
|
|
}
|
|
|
|
hasher := sigAlg.HashID().New()
|
|
_, err = io.Copy(hasher, data)
|
|
if err != nil {
|
|
return fmt.Errorf("error reading data to sign: %s", err)
|
|
}
|
|
hash := hasher.Sum(nil)
|
|
|
|
err = rsa.VerifyPKCS1v15(k.PublicKey, sigAlg.HashID(), hash, signature)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid %s signature: %s", sigAlg.HeaderParam(), err)
|
|
}
|
|
|
|
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 *rsaPublicKey) CryptoPublicKey() crypto.PublicKey {
|
|
return k.PublicKey
|
|
}
|
|
|
|
func (k *rsaPublicKey) 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["n"] = joseBase64UrlEncode(k.N.Bytes())
|
|
jwk["e"] = joseBase64UrlEncode(serializeRSAPublicExponentParam(k.E))
|
|
|
|
return jwk
|
|
}
|
|
|
|
// MarshalJSON serializes this Public Key using the JWK JSON serialization format for
|
|
// RSA keys.
|
|
func (k *rsaPublicKey) MarshalJSON() (data []byte, err error) {
|
|
return json.Marshal(k.toMap())
|
|
}
|
|
|
|
// PEMBlock serializes this Public Key to DER-encoded PKIX format.
|
|
func (k *rsaPublicKey) PEMBlock() (*pem.Block, error) {
|
|
derBytes, err := x509.MarshalPKIXPublicKey(k.PublicKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to serialize RSA 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 *rsaPublicKey) AddExtendedField(field string, value interface{}) {
|
|
k.extended[field] = value
|
|
}
|
|
|
|
func (k *rsaPublicKey) GetExtendedField(field string) interface{} {
|
|
v, ok := k.extended[field]
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return v
|
|
}
|
|
|
|
func rsaPublicKeyFromMap(jwk map[string]interface{}) (*rsaPublicKey, error) {
|
|
// JWK key type (kty) has already been determined to be "RSA".
|
|
// Need to extract 'n', 'e', and 'kid' and check for
|
|
// consistency.
|
|
|
|
// Get the modulus parameter N.
|
|
nB64Url, err := stringFromMap(jwk, "n")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Public Key modulus: %s", err)
|
|
}
|
|
|
|
n, err := parseRSAModulusParam(nB64Url)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Public Key modulus: %s", err)
|
|
}
|
|
|
|
// Get the public exponent E.
|
|
eB64Url, err := stringFromMap(jwk, "e")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Public Key exponent: %s", err)
|
|
}
|
|
|
|
e, err := parseRSAPublicExponentParam(eB64Url)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Public Key exponent: %s", err)
|
|
}
|
|
|
|
key := &rsaPublicKey{
|
|
PublicKey: &rsa.PublicKey{N: n, E: e},
|
|
}
|
|
|
|
// Key ID is optional, 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 RSA Public Key ID: %s", err)
|
|
}
|
|
if kid != key.KeyID() {
|
|
return nil, fmt.Errorf("JWK RSA Public Key ID does not match: %s", kid)
|
|
}
|
|
}
|
|
|
|
if _, ok := jwk["d"]; ok {
|
|
return nil, fmt.Errorf("JWK RSA Public Key cannot contain private exponent")
|
|
}
|
|
|
|
key.extended = jwk
|
|
|
|
return key, nil
|
|
}
|
|
|
|
/*
|
|
* RSA DSA PRIVATE KEY
|
|
*/
|
|
|
|
// rsaPrivateKey implements a JWK Private Key using RSA digital signature algorithms.
|
|
type rsaPrivateKey struct {
|
|
rsaPublicKey
|
|
*rsa.PrivateKey
|
|
}
|
|
|
|
func fromRSAPrivateKey(cryptoPrivateKey *rsa.PrivateKey) *rsaPrivateKey {
|
|
return &rsaPrivateKey{
|
|
*fromRSAPublicKey(&cryptoPrivateKey.PublicKey),
|
|
cryptoPrivateKey,
|
|
}
|
|
}
|
|
|
|
// PublicKey returns the Public Key data associated with this Private Key.
|
|
func (k *rsaPrivateKey) PublicKey() PublicKey {
|
|
return &k.rsaPublicKey
|
|
}
|
|
|
|
func (k *rsaPrivateKey) String() string {
|
|
return fmt.Sprintf("RSA Private Key <%s>", k.KeyID())
|
|
}
|
|
|
|
// Sign signs the data read from the io.Reader using a signature algorithm supported
|
|
// by the RSA 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., "RS256", "RS384",
|
|
// "RS512".
|
|
func (k *rsaPrivateKey) Sign(data io.Reader, hashID crypto.Hash) (signature []byte, alg string, err error) {
|
|
// Generate a signature of the data using the internal alg.
|
|
sigAlg := rsaPKCS1v15SignatureAlgorithmForHashID(hashID)
|
|
hasher := sigAlg.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)
|
|
|
|
signature, err = rsa.SignPKCS1v15(rand.Reader, k.PrivateKey, sigAlg.HashID(), hash)
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("error producing signature: %s", err)
|
|
}
|
|
|
|
alg = sigAlg.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 *rsaPrivateKey) CryptoPrivateKey() crypto.PrivateKey {
|
|
return k.PrivateKey
|
|
}
|
|
|
|
func (k *rsaPrivateKey) toMap() map[string]interface{} {
|
|
k.Precompute() // Make sure the precomputed values are stored.
|
|
jwk := k.rsaPublicKey.toMap()
|
|
|
|
jwk["d"] = joseBase64UrlEncode(k.D.Bytes())
|
|
jwk["p"] = joseBase64UrlEncode(k.Primes[0].Bytes())
|
|
jwk["q"] = joseBase64UrlEncode(k.Primes[1].Bytes())
|
|
jwk["dp"] = joseBase64UrlEncode(k.Precomputed.Dp.Bytes())
|
|
jwk["dq"] = joseBase64UrlEncode(k.Precomputed.Dq.Bytes())
|
|
jwk["qi"] = joseBase64UrlEncode(k.Precomputed.Qinv.Bytes())
|
|
|
|
otherPrimes := k.Primes[2:]
|
|
|
|
if len(otherPrimes) > 0 {
|
|
otherPrimesInfo := make([]interface{}, len(otherPrimes))
|
|
for i, r := range otherPrimes {
|
|
otherPrimeInfo := make(map[string]string, 3)
|
|
otherPrimeInfo["r"] = joseBase64UrlEncode(r.Bytes())
|
|
crtVal := k.Precomputed.CRTValues[i]
|
|
otherPrimeInfo["d"] = joseBase64UrlEncode(crtVal.Exp.Bytes())
|
|
otherPrimeInfo["t"] = joseBase64UrlEncode(crtVal.Coeff.Bytes())
|
|
otherPrimesInfo[i] = otherPrimeInfo
|
|
}
|
|
jwk["oth"] = otherPrimesInfo
|
|
}
|
|
|
|
return jwk
|
|
}
|
|
|
|
// MarshalJSON serializes this Private Key using the JWK JSON serialization format for
|
|
// RSA keys.
|
|
func (k *rsaPrivateKey) MarshalJSON() (data []byte, err error) {
|
|
return json.Marshal(k.toMap())
|
|
}
|
|
|
|
// PEMBlock serializes this Private Key to DER-encoded PKIX format.
|
|
func (k *rsaPrivateKey) PEMBlock() (*pem.Block, error) {
|
|
derBytes := x509.MarshalPKCS1PrivateKey(k.PrivateKey)
|
|
k.extended["keyID"] = k.KeyID() // For display purposes.
|
|
return createPemBlock("RSA PRIVATE KEY", derBytes, k.extended)
|
|
}
|
|
|
|
func rsaPrivateKeyFromMap(jwk map[string]interface{}) (*rsaPrivateKey, error) {
|
|
// The JWA spec for RSA Private Keys (draft rfc section 5.3.2) states that
|
|
// only the private key exponent 'd' is REQUIRED, the others are just for
|
|
// signature/decryption optimizations and SHOULD be included when the JWK
|
|
// is produced. We MAY choose to accept a JWK which only includes 'd', but
|
|
// we're going to go ahead and not choose to accept it without the extra
|
|
// fields. Only the 'oth' field will be optional (for multi-prime keys).
|
|
privateExponent, err := parseRSAPrivateKeyParamFromMap(jwk, "d")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key exponent: %s", err)
|
|
}
|
|
firstPrimeFactor, err := parseRSAPrivateKeyParamFromMap(jwk, "p")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err)
|
|
}
|
|
secondPrimeFactor, err := parseRSAPrivateKeyParamFromMap(jwk, "q")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err)
|
|
}
|
|
firstFactorCRT, err := parseRSAPrivateKeyParamFromMap(jwk, "dp")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err)
|
|
}
|
|
secondFactorCRT, err := parseRSAPrivateKeyParamFromMap(jwk, "dq")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err)
|
|
}
|
|
crtCoeff, err := parseRSAPrivateKeyParamFromMap(jwk, "qi")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key CRT coefficient: %s", err)
|
|
}
|
|
|
|
var oth interface{}
|
|
if _, ok := jwk["oth"]; ok {
|
|
oth = jwk["oth"]
|
|
delete(jwk, "oth")
|
|
}
|
|
|
|
// JWK key type (kty) has already been determined to be "RSA".
|
|
// Need to extract the public key information, then extract the private
|
|
// key values.
|
|
publicKey, err := rsaPublicKeyFromMap(jwk)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
privateKey := &rsa.PrivateKey{
|
|
PublicKey: *publicKey.PublicKey,
|
|
D: privateExponent,
|
|
Primes: []*big.Int{firstPrimeFactor, secondPrimeFactor},
|
|
Precomputed: rsa.PrecomputedValues{
|
|
Dp: firstFactorCRT,
|
|
Dq: secondFactorCRT,
|
|
Qinv: crtCoeff,
|
|
},
|
|
}
|
|
|
|
if oth != nil {
|
|
// Should be an array of more JSON objects.
|
|
otherPrimesInfo, ok := oth.([]interface{})
|
|
if !ok {
|
|
return nil, errors.New("JWK RSA Private Key: Invalid other primes info: must be an array")
|
|
}
|
|
numOtherPrimeFactors := len(otherPrimesInfo)
|
|
if numOtherPrimeFactors == 0 {
|
|
return nil, errors.New("JWK RSA Privake Key: Invalid other primes info: must be absent or non-empty")
|
|
}
|
|
otherPrimeFactors := make([]*big.Int, numOtherPrimeFactors)
|
|
productOfPrimes := new(big.Int).Mul(firstPrimeFactor, secondPrimeFactor)
|
|
crtValues := make([]rsa.CRTValue, numOtherPrimeFactors)
|
|
|
|
for i, val := range otherPrimesInfo {
|
|
otherPrimeinfo, ok := val.(map[string]interface{})
|
|
if !ok {
|
|
return nil, errors.New("JWK RSA Private Key: Invalid other prime info: must be a JSON object")
|
|
}
|
|
|
|
otherPrimeFactor, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "r")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key prime factor: %s", err)
|
|
}
|
|
otherFactorCRT, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "d")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key CRT exponent: %s", err)
|
|
}
|
|
otherCrtCoeff, err := parseRSAPrivateKeyParamFromMap(otherPrimeinfo, "t")
|
|
if err != nil {
|
|
return nil, fmt.Errorf("JWK RSA Private Key CRT coefficient: %s", err)
|
|
}
|
|
|
|
crtValue := crtValues[i]
|
|
crtValue.Exp = otherFactorCRT
|
|
crtValue.Coeff = otherCrtCoeff
|
|
crtValue.R = productOfPrimes
|
|
otherPrimeFactors[i] = otherPrimeFactor
|
|
productOfPrimes = new(big.Int).Mul(productOfPrimes, otherPrimeFactor)
|
|
}
|
|
|
|
privateKey.Primes = append(privateKey.Primes, otherPrimeFactors...)
|
|
privateKey.Precomputed.CRTValues = crtValues
|
|
}
|
|
|
|
key := &rsaPrivateKey{
|
|
rsaPublicKey: *publicKey,
|
|
PrivateKey: privateKey,
|
|
}
|
|
|
|
return key, nil
|
|
}
|
|
|
|
/*
|
|
* Key Generation Functions.
|
|
*/
|
|
|
|
func generateRSAPrivateKey(bits int) (k *rsaPrivateKey, err error) {
|
|
k = new(rsaPrivateKey)
|
|
k.PrivateKey, err = rsa.GenerateKey(rand.Reader, bits)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
k.rsaPublicKey.PublicKey = &k.PrivateKey.PublicKey
|
|
k.extended = make(map[string]interface{})
|
|
|
|
return
|
|
}
|
|
|
|
// GenerateRSA2048PrivateKey generates a key pair using 2048-bit RSA.
|
|
func GenerateRSA2048PrivateKey() (PrivateKey, error) {
|
|
k, err := generateRSAPrivateKey(2048)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error generating RSA 2048-bit key: %s", err)
|
|
}
|
|
|
|
return k, nil
|
|
}
|
|
|
|
// GenerateRSA3072PrivateKey generates a key pair using 3072-bit RSA.
|
|
func GenerateRSA3072PrivateKey() (PrivateKey, error) {
|
|
k, err := generateRSAPrivateKey(3072)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error generating RSA 3072-bit key: %s", err)
|
|
}
|
|
|
|
return k, nil
|
|
}
|
|
|
|
// GenerateRSA4096PrivateKey generates a key pair using 4096-bit RSA.
|
|
func GenerateRSA4096PrivateKey() (PrivateKey, error) {
|
|
k, err := generateRSAPrivateKey(4096)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error generating RSA 4096-bit key: %s", err)
|
|
}
|
|
|
|
return k, nil
|
|
}
|