Added support for RSASSA-PSS and RSAES-OAEP.

This commit is contained in:
InfiniteLoopSpace
2018-12-21 14:43:59 +01:00
parent 5f34d82562
commit 3f58f9a4b2
10 changed files with 510 additions and 181 deletions

183
cms/protocol/pssoaep.go Normal file
View File

@ -0,0 +1,183 @@
package protocol
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"errors"
oid "github.com/InfiniteLoopSpace/go_S-MIME/oid"
)
type pssParameters struct {
Hash pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:0"`
MGF pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:1"`
SaltLength int `asn1:"optional,explicit,tag:2"`
TrailerField int `asn1:"optional,explicit,tag:3"` //,default:1"`
}
var oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
func newpssParameters(hash ...crypto.Hash) (param pssParameters, err error) {
// SHA1 is default value
if len(hash) == 1 && hash[0] == crypto.SHA1 {
return
}
var h asn1.ObjectIdentifier
if len(hash) == 0 {
h = oid.DigestAlgorithmSHA1
param.SaltLength = 20
param.TrailerField = 1
} else {
var ok bool
h, ok = oid.HashToDigestAlgorithm[hash[0]]
if !ok {
err = errors.New("Unsupported hashfunction")
}
}
hRV, err := RawValue(pkix.AlgorithmIdentifier{Algorithm: h})
if err != nil {
return
}
param.Hash = pkix.AlgorithmIdentifier{Algorithm: h}
param.MGF = pkix.AlgorithmIdentifier{Algorithm: oidMGF1, Parameters: hRV}
return
}
// This is needed because CheckSignature uses PSSSaltLengthEqualsHash, if PSSSaltLengthAuto is used this code is not needed
func verfiyRSAPSS(cert x509.Certificate, signatureAlgorithm pkix.AlgorithmIdentifier, signedMessage, signature []byte) (err error) {
param, err := newpssParameters()
if err != nil {
return
}
_, err = asn1.Unmarshal(signatureAlgorithm.Parameters.FullBytes, &param)
if err != nil {
return
}
if !param.MGF.Algorithm.Equal(oidMGF1) {
err = errors.New("Mask generator funktion not supported; only MGF1 is supported")
return
}
hash := oid.DigestAlgorithmToHash[param.Hash.Algorithm.String()]
pssOpts := rsa.PSSOptions{SaltLength: param.SaltLength, Hash: hash}
h := hash.New()
h.Write(signedMessage)
digest := h.Sum(nil)
err = rsa.VerifyPSS(cert.PublicKey.(*rsa.PublicKey), hash, digest, signature, &pssOpts)
return
}
func newPSS(hash crypto.Hash, pub *rsa.PublicKey) (signatureAlgorithm pkix.AlgorithmIdentifier, opts *rsa.PSSOptions, err error) {
opts = &rsa.PSSOptions{Hash: hash}
pssParam, err := newpssParameters(hash)
if err != nil {
return
}
pssParam.SaltLength = (pub.N.BitLen()+7)/8 - 2 - hash.Size() // https://golang.org/src/crypto/rsa/pss.go?s=6982:7095#L239
paramRV, err := RawValue(pssParam)
if err != nil {
return
}
signatureAlgorithm = pkix.AlgorithmIdentifier{Algorithm: oid.SignatureAlgorithmRSASSAPSS, Parameters: paramRV}
return
}
// RSAESOAEPparams ::= SEQUENCE {
// hashFunc [0] AlgorithmIdentifier DEFAULT sha1Identifier,
// maskGenFunc [1] AlgorithmIdentifier DEFAULT mgf1SHA1Identifier,
// pSourceFunc [2] AlgorithmIdentifier DEFAULT
// pSpecifiedEmptyIdentifier }
type RSAESOAEPparams struct {
HashFunc pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:0"`
MaskGenFunc pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:1"`
PSourceFunc pkix.AlgorithmIdentifier `asn1:"optional,explicit,tag:2"`
}
var oidpSpecified = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 9}
func newRSAESOAEPparams(hash ...crypto.Hash) (param RSAESOAEPparams, err error) {
// SHA1 is default value
if len(hash) == 1 && hash[0] == crypto.SHA1 {
return
}
nullOctetString, err := RawValue([]byte{})
var h asn1.ObjectIdentifier
if len(hash) == 0 {
h = oid.DigestAlgorithmSHA1
} else {
var ok bool
h, ok = oid.HashToDigestAlgorithm[hash[0]]
if !ok {
err = errors.New("Unsupported hashfunction")
}
}
hRV, err := RawValue(pkix.AlgorithmIdentifier{Algorithm: h})
param = RSAESOAEPparams{pkix.AlgorithmIdentifier{Algorithm: h, Parameters: asn1.NullRawValue},
pkix.AlgorithmIdentifier{Algorithm: oidMGF1, Parameters: hRV},
pkix.AlgorithmIdentifier{Algorithm: oidpSpecified, Parameters: nullOctetString}}
return
}
func parseRSAESOAEPparams(param []byte) (opts *rsa.OAEPOptions, err error) {
var oaepOpts RSAESOAEPparams
oaepOpts, err = newRSAESOAEPparams()
if err != nil {
return
}
_, err = asn1.Unmarshal(param, &oaepOpts)
if err != nil {
return
}
opts = &rsa.OAEPOptions{Hash: oid.DigestAlgorithmToHash[oaepOpts.HashFunc.Algorithm.String()], Label: []byte{}}
if !oaepOpts.MaskGenFunc.Algorithm.Equal(oidMGF1) {
err = errors.New("Unsupported mask generation funktion" + oaepOpts.MaskGenFunc.Algorithm.String())
return
}
if !oaepOpts.PSourceFunc.Algorithm.Equal(oidpSpecified) {
err = errors.New("Unsupported p source funktion" + oaepOpts.PSourceFunc.Algorithm.String())
return
}
return
}
func isRSAPSS(cert *x509.Certificate) bool {
switch cert.SignatureAlgorithm {
case x509.SHA256WithRSAPSS, x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS:
return true
default:
return false
}
}

View File

@ -10,7 +10,6 @@ import (
"crypto/x509/pkix"
"encoding/asn1"
"errors"
"fmt"
"log"
"time"
@ -34,7 +33,7 @@ type RecipientInfo struct {
func (recInfo *RecipientInfo) decryptKey(keyPair tls.Certificate) (key []byte, err error) {
key, err = recInfo.KTRI.decryptKey(keyPair)
if key != nil {
if key != nil || (err != nil && err != ErrUnsupported) {
return
}
@ -64,6 +63,23 @@ func (ktri *KeyTransRecipientInfo) decryptKey(keyPair tls.Certificate) (key []by
ski := keyPair.Leaf.SubjectKeyId
certPubAlg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm
var decOpts crypto.DecrypterOpts
pkcs15CertwithOAEP := false
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(oid.EncryptionAlgorithmRSAESOAEP) {
if certPubAlg.Equal(oid.EncryptionAlgorithmRSA) {
pkcs15CertwithOAEP = true
}
decOpts, err = parseRSAESOAEPparams(ktri.KeyEncryptionAlgorithm.Parameters.FullBytes)
if err != nil {
return
}
}
//version is the syntax version number. If the SignerIdentifier is
//the CHOICE issuerAndSerialNumber, then the version MUST be 1. If
//the SignerIdentifier is subjectKeyIdentifier, then the version
@ -71,28 +87,25 @@ func (ktri *KeyTransRecipientInfo) decryptKey(keyPair tls.Certificate) (key []by
switch ktri.Version {
case 0:
if ias.Equal(ktri.Rid.IAS) {
alg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(alg) {
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(certPubAlg) || pkcs15CertwithOAEP {
decrypter := keyPair.PrivateKey.(crypto.Decrypter)
return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, nil)
return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, decOpts)
}
log.Println("Key encrytion algorithm not matching")
}
case 2:
if bytes.Equal(ski, ktri.Rid.SKI) {
alg := oid.PublicKeyAlgorithmToEncrytionAlgorithm[keyPair.Leaf.PublicKeyAlgorithm].Algorithm
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(alg) {
if alg.Equal(oid.EncryptionAlgorithmRSA) {
return rsa.DecryptPKCS1v15(rand.Reader, keyPair.PrivateKey.(*rsa.PrivateKey), ktri.EncryptedKey)
}
log.Println("Unsupported key encrytion algorithm")
if ktri.KeyEncryptionAlgorithm.Algorithm.Equal(certPubAlg) || pkcs15CertwithOAEP {
decrypter := keyPair.PrivateKey.(crypto.Decrypter)
return decrypter.Decrypt(rand.Reader, ktri.EncryptedKey, decOpts)
}
log.Println("Key encrytion algorithm not matching")
}
default:
fmt.Println(ktri.Version)
return nil, ErrUnsupported
}
@ -109,35 +122,15 @@ type RecipientIdentifier struct {
// NewRecipientInfo creates RecipientInfo for giben recipient and key.
func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientInfo, err error) {
version := 0 //issuerAndSerialNumber
rid := RecipientIdentifier{}
switch version {
case 0:
ias, err := NewIssuerAndSerialNumber(recipient)
if err != nil {
log.Fatal(err)
}
rid.IAS = ias
case 2:
rid.SKI = recipient.SubjectKeyId
}
switch recipient.PublicKeyAlgorithm {
case x509.RSA:
var encrypted []byte
encrypted, err = encryptKeyRSA(key, recipient)
var ktri KeyTransRecipientInfo
ktri, err = encryptKeyRSA(key, recipient)
if err != nil {
return
}
info = RecipientInfo{
KTRI: KeyTransRecipientInfo{
Version: version,
Rid: rid,
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSA},
EncryptedKey: encrypted,
}}
info = RecipientInfo{KTRI: ktri}
case x509.ECDSA:
var kari KeyAgreeRecipientInfo
kari, err = encryptKeyECDH(key, recipient)
@ -152,11 +145,47 @@ func NewRecipientInfo(recipient *x509.Certificate, key []byte) (info RecipientIn
return
}
func encryptKeyRSA(key []byte, recipient *x509.Certificate) ([]byte, error) {
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
func encryptKeyRSA(key []byte, recipient *x509.Certificate) (ktri KeyTransRecipientInfo, err error) {
ktri.Version = 0 //issuerAndSerialNumber
switch ktri.Version {
case 0:
ias, err := NewIssuerAndSerialNumber(recipient)
if err != nil {
log.Fatal(err)
}
ktri.Rid.IAS = ias
case 2:
ktri.Rid.SKI = recipient.SubjectKeyId
}
return nil, ErrUnsupportedAlgorithm
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
if isRSAPSS(recipient) {
hash := crypto.SHA256
var oaepparam RSAESOAEPparams
oaepparam, err = newRSAESOAEPparams(hash)
if err != nil {
return
}
var oaepparamRV asn1.RawValue
oaepparamRV, err = RawValue(oaepparam)
if err != nil {
return
}
ktri.KeyEncryptionAlgorithm = pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSAESOAEP, Parameters: oaepparamRV}
h := hash.New()
ktri.EncryptedKey, err = rsa.EncryptOAEP(h, rand.Reader, pub, key, nil)
return
}
ktri.KeyEncryptionAlgorithm = pkix.AlgorithmIdentifier{Algorithm: oid.EncryptionAlgorithmRSA}
ktri.EncryptedKey, err = rsa.EncryptPKCS1v15(rand.Reader, pub, key)
return
}
err = ErrUnsupportedAlgorithm
return
}
// ErrUnsupportedAlgorithm is returned if the algorithm is unsupported.

View File

@ -6,6 +6,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
@ -106,7 +107,7 @@ func NewSignedData(eci EncapsulatedContentInfo) (*SignedData, error) {
}
// AddSignerInfo adds a SignerInfo to the SignedData.
func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate, attrs []Attribute) (err error) {
for _, cert := range keypPair.Certificate {
if err = sd.AddCertificate(cert); err != nil {
@ -125,8 +126,13 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
sid := SignerIdentifier{ias, nil}
var signerOpts crypto.SignerOpts
digestAlgorithm := digestAlgorithmForPublicKey(cert.PublicKey)
signatureAlgorithm, ok := oid.PublicKeyAlgorithmToSignatureAlgorithm[keypPair.Leaf.PublicKeyAlgorithm]
if isRSAPSS(cert) {
h := oid.DigestAlgorithmToHash[digestAlgorithm.Algorithm.String()]
signatureAlgorithm, signerOpts, err = newPSS(h, cert.PublicKey.(*rsa.PublicKey))
}
if !ok {
return errors.New("unsupported certificate public key algorithm")
}
@ -155,6 +161,11 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
if err != nil {
return err
}
if !isRSAPSS(cert) {
signerOpts = hash
}
md := hash.New()
if _, err = md.Write(content); err != nil {
return err
@ -174,6 +185,7 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
return err
}
si.SignedAttrs = append(si.SignedAttrs, mdAttr, ctAttr, sTAttr)
si.SignedAttrs = append(si.SignedAttrs, attrs...)
sm, err := asn.MarshalWithParams(si.SignedAttrs, `set`)
if err != nil {
@ -184,7 +196,7 @@ func (sd *SignedData) AddSignerInfo(keypPair tls.Certificate) (err error) {
if _, errr := smd.Write(sm); errr != nil {
return errr
}
if si.Signature, err = signer.Sign(rand.Reader, smd.Sum(nil), hash); err != nil {
if si.Signature, err = signer.Sign(rand.Reader, smd.Sum(nil), signerOpts); err != nil {
return err
}
@ -384,7 +396,11 @@ func (sd *SignedData) Verify(Opts x509.VerifyOptions, detached []byte) (chains [
if err != nil {
return
}
err = cert.CheckSignature(sigAlg, signedMessage, signer.Signature)
switch signer.SignatureAlgorithm.Algorithm.String() {
case oid.SignatureAlgorithmRSASSAPSS.String():
default:
err = cert.CheckSignature(sigAlg, signedMessage, signer.Signature)
}
if err != nil {
return
}