diff --git a/go.mod b/go.mod index e7eae35..fe8c680 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/peterbourgon/ff/v4 v4.0.0-alpha.4 github.com/sigurn/crc16 v0.0.0-20211026045750-20ab5afb07e3 go.uber.org/zap v1.26.0 + golang.org/x/crypto v0.18.0 ) require go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index da5b6be..ceaba59 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/pkcs15/encrypted_envelope.go b/pkg/pkcs15/encrypted_envelope.go new file mode 100644 index 0000000..7e0acc8 --- /dev/null +++ b/pkg/pkcs15/encrypted_envelope.go @@ -0,0 +1,219 @@ +package pkcs15 + +import ( + "apc-p15-tool/pkg/tools" + "apc-p15-tool/pkg/tools/asn1obj" + "crypto/cipher" + "crypto/des" + "crypto/hmac" + "crypto/rand" + "crypto/sha1" + "crypto/sha256" + "encoding/asn1" + "math/big" + + "golang.org/x/crypto/pbkdf2" +) + +// fixed specs for apc cert +const ( + apcKEKPassword = "user" + apcKEKIterations = 5000 +) + +// encryptedKeyEnvelope encrypts p15's rsa private key using the algorithms and +// params expected in the APC file. Salt values are always random. +func (p15 *pkcs15KeyCert) encryptedKeyEnvelope() ([]byte, error) { + // calculate values for the object + kekSalt := make([]byte, 8) + _, err := rand.Read(kekSalt) + if err != nil { + return nil, err + } + + // kek hash alg + kekHash := sha256.New + // size of 3DES key (k1 + k2 + k3) + kekSize := 24 + + // kek + kek := pbkdf2.Key([]byte(apcKEKPassword), kekSalt, apcKEKIterations, kekSize, kekHash) + + // make DES cipher from KEK for CEK + cekDesCipher, err := des.NewTripleDESCipher(kek) + if err != nil { + return nil, err + } + + // cek (16 bytes for authEnc128) -- see: rfc3211 + cekLen := uint8(16) + cek := make([]byte, cekLen) + _, err = rand.Read(cek) + if err != nil { + return nil, err + } + + // LEN + Check Val [3] + wrappedCEK := append([]byte{cekLen}, tools.BitwiseComplimentOf(cek[:3])...) + + // + CEK + wrappedCEK = append(wrappedCEK, cek...) + + // + padding (if needed) + // pad wrapped CEK to min 2 * block len + cekPadLen := 0 + if len(wrappedCEK) < 2*cekDesCipher.BlockSize() { + cekPadLen = 2*cekDesCipher.BlockSize() - len(wrappedCEK) + } else if len(wrappedCEK)%cekDesCipher.BlockSize() != 0 { + // pad if not a multiple of block len + cekPadLen = cekDesCipher.BlockSize() - len(wrappedCEK)%cekDesCipher.BlockSize() + } + cekPadding := make([]byte, cekPadLen) + _, err = rand.Read(cekPadding) + if err != nil { + return nil, err + } + wrappedCEK = append(wrappedCEK, cekPadding...) + + // double encrypt CEK + cekEncryptSalt := make([]byte, 8) + _, err = rand.Read(cekEncryptSalt) + if err != nil { + return nil, err + } + + cekEncrypter := cipher.NewCBCEncrypter(cekDesCipher, cekEncryptSalt) + + encryptedCEKOnly1Rd := make([]byte, len(wrappedCEK)) + cekEncrypter.CryptBlocks(encryptedCEKOnly1Rd, wrappedCEK) + encryptedCEK := make([]byte, len(encryptedCEKOnly1Rd)) + cekEncrypter.CryptBlocks(encryptedCEK, encryptedCEKOnly1Rd) + + // content encryption + contentEncSalt := make([]byte, 8) + _, err = rand.Read(contentEncSalt) + if err != nil { + return nil, err + } + + contentEncryptKey := pbkdf2.Key(cek, []byte("encryption"), 1, 24, sha1.New) + contentDesCipher, err := des.NewTripleDESCipher(contentEncryptKey) + if err != nil { + return nil, err + } + + content := p15.privateKeyObject() + // pad content, see: https://datatracker.ietf.org/doc/html/rfc3852 6.3 + contentPadLen := uint8(contentDesCipher.BlockSize() - (len(content) % contentDesCipher.BlockSize())) + // ALWAYS pad, if content is exact, add full block of padding + if contentPadLen == 0 { + contentPadLen = uint8(contentDesCipher.BlockSize()) + } + for i := uint8(1); i <= contentPadLen; i++ { + content = append(content, byte(contentPadLen)) + } + + contentEncrypter := cipher.NewCBCEncrypter(contentDesCipher, contentEncSalt) + encryptedContent := make([]byte, len(content)) + contentEncrypter.CryptBlocks(encryptedContent, content) + + // encrypted content MAC + macKey := pbkdf2.Key(cek, []byte("authentication"), 1, 32, sha1.New) + + // data encryption alg block + encAlgObj := asn1obj.Sequence([][]byte{ + // ContentEncryptionAlgorithmIdentifier + asn1obj.ObjectIdentifier(asn1obj.OIDauthEnc128), + // ContentEncryptionAlgorithmIdentifier details/info + asn1obj.Sequence([][]byte{ + // encryption alg & salt + asn1obj.Sequence([][]byte{ + // encryption alg + asn1obj.ObjectIdentifier(asn1obj.OIDdesEDE3CBC), + // encryption alg's salt + asn1obj.OctetString(contentEncSalt), + }), + // mac alg + asn1obj.Sequence([][]byte{ + asn1obj.ObjectIdentifier(asn1obj.OIDhmacWithSHA256), + asn1.NullBytes, + }), + }), + }) + + macHasher := hmac.New(sha256.New, macKey) + // the data the MAC covers is the algId header bytes + encrypted data bytes + hashMe := append(encAlgObj, encryptedContent...) + + // make MAC + _, err = macHasher.Write(hashMe) + if err != nil { + return nil, err + } + mac := macHasher.Sum(nil) + + // build object + // AuthEnvelopedData Type + envelope := [][]byte{ + // CMSVersion + asn1obj.Integer(big.NewInt(2)), + // RecipientInfos + asn1obj.Set([][]byte{ + // 1st and only 'RecipientInfo' - pwri [3] PasswordRecipientinfo + asn1obj.ExplicitCompound(3, [][]byte{ + // CMSVersion + asn1obj.Integer(big.NewInt(0)), + // keyDerivationAlgorithm [0] + asn1obj.ExplicitCompound(0, [][]byte{ + // KeyDerivationAlgorithmIdentifier + asn1obj.ObjectIdentifier(asn1obj.OIDpkcs5PBKDF2), + // KeyDerivationAlgorithmIdentifier details/info + asn1obj.Sequence([][]byte{ + // kek pbkdf2 Salt + asn1obj.OctetString(kekSalt), + // kek pbkdf2 Iterations + asn1obj.Integer(big.NewInt(apcKEKIterations)), + // kek pbkdf2 hash type + asn1obj.Sequence([][]byte{ + asn1obj.ObjectIdentifier(asn1obj.OIDhmacWithSHA256), + asn1.NullBytes, + }), + }), + }), + // keyEncryptionAlgorithm (for CEK) + asn1obj.Sequence([][]byte{ + // KeyEncryptionAlgorithmIdentifier + asn1obj.ObjectIdentifier(asn1obj.OIDpwriKEK), + // KeyEncryptionAlgorithmIdentifier details/info + asn1obj.Sequence([][]byte{ + // encryption alg + asn1obj.ObjectIdentifier(asn1obj.OIDdesEDE3CBC), + // encryption alg's salt + asn1obj.OctetString(cekEncryptSalt), + }), + }), + // EncryptedKey (the actual ciphertext for the CEK) + asn1obj.OctetString(encryptedCEK), + }), + }), + // EncryptedContentInfo (actual encrypted content) + asn1obj.Sequence([][]byte{ + // ContentType + asn1obj.ObjectIdentifier(asn1obj.OIDpkcs7Data), + // encryption alg OBJ + encAlgObj, + // [0] IMPLICIT EncryptedContent (AKA the ciphertext) + asn1obj.ExplicitValue(0, encryptedContent), + }), + // MAC + asn1obj.OctetString(mac), + } + + // combine to singular byte slice + finalEnv := []byte{} + for i := range envelope { + finalEnv = append(finalEnv, envelope[i]...) + } + + return finalEnv, nil +} diff --git a/pkg/pkcs15/keyid.go b/pkg/pkcs15/keyid.go index 3eb14f2..cf586f3 100644 --- a/pkg/pkcs15/keyid.go +++ b/pkg/pkcs15/keyid.go @@ -3,6 +3,7 @@ package pkcs15 import ( "apc-p15-tool/pkg/tools/asn1obj" "crypto/sha1" + "encoding/asn1" "math/big" ) @@ -13,7 +14,7 @@ func (p15 *pkcs15KeyCert) keyId() []byte { asn1obj.Sequence([][]byte{ // Key is RSA asn1obj.ObjectIdentifier(asn1obj.OIDrsaEncryptionPKCS1), - asn1obj.Null(), + asn1.NullBytes, }), // BIT STRING of rsa key public key asn1obj.BitString( @@ -33,3 +34,19 @@ func (p15 *pkcs15KeyCert) keyId() []byte { return hasher.Sum(nil) } + +// keyIdInt2 returns the sequence for keyId with INT val of 2 +// For APC, this appears to be the same value is the base keyId +// but this isn't compliant with the spec which actually seems +// to call for SKID (skid octet value copied directly out of the +// certificate's x509 extension) +func (p15 *pkcs15KeyCert) keyIdInt2() []byte { + // Create Object + obj := asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(2)), + // Note: This is for APC, doesn't seem compliant with spec though + asn1obj.OctetString(p15.keyId()), + }) + + return obj +} diff --git a/pkg/pkcs15/marshal.go b/pkg/pkcs15/marshal.go deleted file mode 100644 index b2eeaf1..0000000 --- a/pkg/pkcs15/marshal.go +++ /dev/null @@ -1,66 +0,0 @@ -package pkcs15 - -import ( - "apc-p15-tool/pkg/tools/asn1obj" - "math/big" -) - -const ( - apcKeyLabel = "Private key" -) - -// ToP15File turns the key and cert into a properly formatted and encoded -// p15 file -func (p15 *pkcs15KeyCert) ToP15File() ([]byte, error) { - // private key object - pkey, err := p15.toP15PrivateKey() - if err != nil { - return nil, err - } - - // ContentInfo - p15File := asn1obj.Sequence([][]byte{ - - // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) - asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), - - // content - asn1obj.Explicit(0, - asn1obj.Sequence([][]byte{ - asn1obj.Integer(big.NewInt(0)), - asn1obj.Sequence([][]byte{ - asn1obj.Explicit(0, - asn1obj.Explicit(0, - pkey, - ), - ), - }), - }), - ), - }) - - return p15File, nil -} - -// toP15PrivateKey creates the encoded private key. it is broken our from the larger p15 -// function for readability -func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) { - // key object - key := asn1obj.Sequence([][]byte{ - // commonObjectAttributes - Label - asn1obj.Sequence([][]byte{ - asn1obj.UTF8String(apcKeyLabel), - }), - // CommonKeyAttributes - asn1obj.Sequence([][]byte{ - // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) - asn1obj.OctetString(p15.keyId()), - // CommonKeyAttributes - usage (trailing 0s will drop) - asn1obj.BitString([]byte{byte(0b11100010)}), - // CommonKeyAttributes - accessFlags (trailing 0s will drop) - asn1obj.BitString([]byte{byte(0b10110000)}), - }), - }) - - return key, nil -} diff --git a/pkg/pkcs15/pem_parse.go b/pkg/pkcs15/pem_parse.go new file mode 100644 index 0000000..dcd899a --- /dev/null +++ b/pkg/pkcs15/pem_parse.go @@ -0,0 +1,36 @@ +package pkcs15 + +import ( + "crypto/rsa" + "crypto/x509" +) + +// pkcs15KeyCert holds the data for a key and certificate pair; it provides +// various methods to transform pkcs15 data +type pkcs15KeyCert struct { + key *rsa.PrivateKey + cert *x509.Certificate +} + +// ParsePEMToPKCS15 parses the provide pem files to a pkcs15 struct; it also does some +// basic sanity check; if any of this fails, an error is returned +func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) { + // decode / check key + key, err := pemKeyDecode(keyPem) + if err != nil { + return nil, err + } + + // decode / check cert + cert, err := pemCertDecode(certPem, keyPem) + if err != nil { + return nil, err + } + + p15 := &pkcs15KeyCert{ + key: key, + cert: cert, + } + + return p15, nil +} diff --git a/pkg/pkcs15/pem_to_p15.go b/pkg/pkcs15/pem_to_p15.go index dcd899a..61c63ef 100644 --- a/pkg/pkcs15/pem_to_p15.go +++ b/pkg/pkcs15/pem_to_p15.go @@ -1,36 +1,127 @@ package pkcs15 import ( - "crypto/rsa" - "crypto/x509" + "apc-p15-tool/pkg/tools/asn1obj" + "math/big" ) -// pkcs15KeyCert holds the data for a key and certificate pair; it provides -// various methods to transform pkcs15 data -type pkcs15KeyCert struct { - key *rsa.PrivateKey - cert *x509.Certificate -} +const ( + apcKeyLabel = "Private key" +) -// ParsePEMToPKCS15 parses the provide pem files to a pkcs15 struct; it also does some -// basic sanity check; if any of this fails, an error is returned -func ParsePEMToPKCS15(keyPem, certPem []byte) (*pkcs15KeyCert, error) { - // decode / check key - key, err := pemKeyDecode(keyPem) +// ToP15File turns the key and cert into a properly formatted and encoded +// p15 file +func (p15 *pkcs15KeyCert) ToP15File() ([]byte, error) { + // private key object + pkey, err := p15.toP15PrivateKey() if err != nil { return nil, err } - // decode / check cert - cert, err := pemCertDecode(certPem, keyPem) + cert, err := p15.toP15Cert() if err != nil { return nil, err } - p15 := &pkcs15KeyCert{ - key: key, - cert: cert, + // ContentInfo + p15File := asn1obj.Sequence([][]byte{ + + // contentType: OID: 1.2.840.113549.1.15.3.1 pkcs15content (PKCS #15 content type) + asn1obj.ObjectIdentifier(asn1obj.OIDPkscs15Content), + + // content + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.Sequence([][]byte{ + asn1obj.Integer(big.NewInt(0)), + asn1obj.Sequence([][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + pkey, + }), + }), + asn1obj.ExplicitCompound(4, [][]byte{ + asn1obj.ExplicitCompound(0, [][]byte{ + cert, + }), + }), + }), + }), + }), + }) + + return p15File, nil +} + +// toP15PrivateKey creates the encoded private key. it is broken our from the larger p15 +// function for readability +// NOTE: Do not use this to try and turn just a private key into a p15, the format isn't +// quite the same. +func (p15 *pkcs15KeyCert) toP15PrivateKey() ([]byte, error) { + // rsa encrypted key in encrypted envelope + envelope, err := p15.encryptedKeyEnvelope() + if err != nil { + return nil, err } - return p15, nil + // key object + key := asn1obj.Sequence([][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // CommonKeyAttributes + asn1obj.Sequence([][]byte{ + // CommonKeyAttributes - iD - uses keyId that is SHA1( SubjectPublicKeyInfo SEQUENCE ) + asn1obj.OctetString(p15.keyId()), + // CommonKeyAttributes - usage (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b11100010)}), + // CommonKeyAttributes - accessFlags (trailing 0s will drop) + asn1obj.BitString([]byte{byte(0b10110000)}), + // CommonKeyAttributes - startDate + asn1obj.GeneralizedTime(p15.cert.NotBefore), + // CommonKeyAttributes - [0] endDate + asn1obj.GeneralizedTimeExplicitValue(0, p15.cert.NotAfter), + }), + // ObjectValue - indirect-protected + asn1obj.ExplicitCompound(1, [][]byte{ + asn1obj.Sequence([][]byte{ + // AuthEnvelopedData Type ([4]) + asn1obj.ExplicitCompound(4, [][]byte{ + envelope, + }), + }), + }), + }) + + return key, nil +} + +// toP15Cert creates the encoded certificate. it is broken our from the larger p15 +// function for readability +// NOTE: Do not use this to try and turn just a cert into a p15. I don't believe, +// such a thing is permissible under the spec. +func (p15 *pkcs15KeyCert) toP15Cert() ([]byte, error) { + + // cert object + cert := asn1obj.Sequence([][]byte{ + // commonObjectAttributes - Label + asn1obj.Sequence([][]byte{ + asn1obj.UTF8String(apcKeyLabel), + }), + // keyIds of various types + asn1obj.Sequence([][]byte{ + asn1obj.OctetString(p15.keyId()), + // additional keyids + asn1obj.ExplicitCompound(2, [][]byte{ + p15.keyIdInt2(), + // p15.keyIdInt3(), + // p15.keyIdInt6(), + // p15.keyIdInt7(), + // p15.keyIdInt8(), + // p15.keyIdInt9(), + }), + }), + }) + + return cert, nil } diff --git a/pkg/pkcs15/private_key.go b/pkg/pkcs15/private_key.go new file mode 100644 index 0000000..8c6b0fb --- /dev/null +++ b/pkg/pkcs15/private_key.go @@ -0,0 +1,24 @@ +package pkcs15 + +import "apc-p15-tool/pkg/tools/asn1obj" + +// privateKeyObject returns the ASN.1 representation of a private key +func (p15 *pkcs15KeyCert) privateKeyObject() []byte { + // ensure all expected vals are available + p15.key.Precompute() + + pkey := asn1obj.Sequence([][]byte{ + // P + asn1obj.IntegerExplicitValue(3, p15.key.Primes[0]), + // Q + asn1obj.IntegerExplicitValue(4, p15.key.Primes[1]), + // Dp + asn1obj.IntegerExplicitValue(5, p15.key.Precomputed.Dp), + // Dq + asn1obj.IntegerExplicitValue(6, p15.key.Precomputed.Dq), + // Qinv + asn1obj.IntegerExplicitValue(7, p15.key.Precomputed.Qinv), + }) + + return pkey +} diff --git a/pkg/tools/asn1obj/explicit.go b/pkg/tools/asn1obj/explicit.go new file mode 100644 index 0000000..ef12b19 --- /dev/null +++ b/pkg/tools/asn1obj/explicit.go @@ -0,0 +1,46 @@ +package asn1obj + +import "encoding/asn1" + +// ExplicitCompound wraps another ASN.1 Object(s) with the EXPLICIT wrapper using +// the tag number specified +func ExplicitCompound(explicitTagNumber int, wrappedElements [][]byte) []byte { + val := []byte{} + for i := range wrappedElements { + val = append(val, wrappedElements[i]...) + } + + raw := asn1.RawValue{ + Class: asn1.ClassContextSpecific, + Tag: explicitTagNumber, + IsCompound: true, + Bytes: val, + } + + // should never error + asn1result, err := asn1.Marshal(raw) + if err != nil { + panic(err) + } + + return asn1result +} + +// ExplicitValue creates an EXPLICIT Object with a byte data value (i.e. it +// is NOT compound) using the tag number specified +func ExplicitValue(explicitTagNumber int, val []byte) []byte { + raw := asn1.RawValue{ + Class: asn1.ClassContextSpecific, + Tag: explicitTagNumber, + IsCompound: false, + Bytes: val, + } + + // should never error + asn1result, err := asn1.Marshal(raw) + if err != nil { + panic(err) + } + + return asn1result +} diff --git a/pkg/tools/asn1obj/generalizedtime.go b/pkg/tools/asn1obj/generalizedtime.go new file mode 100644 index 0000000..cb25524 --- /dev/null +++ b/pkg/tools/asn1obj/generalizedtime.go @@ -0,0 +1,85 @@ +package asn1obj + +import ( + "encoding/asn1" + "time" +) + +// GeneralizedTime returns the specified time as a GeneralizedTime +func GeneralizedTime(t time.Time) []byte { + // should never error + asn1result, err := asn1.MarshalWithParams(t, "generalized") + if err != nil { + panic(err) + } + + return asn1result +} + +// helper funcs from golang asn1 package +func appendTwoDigits(dst []byte, v int) []byte { + return append(dst, byte('0'+(v/10)%10), byte('0'+v%10)) +} + +func appendFourDigits(dst []byte, v int) []byte { + var bytes [4]byte + for i := range bytes { + bytes[3-i] = '0' + byte(v%10) + v /= 10 + } + return append(dst, bytes[:]...) +} + +// generalizedTimevalue returns the specified time encoded as a +// GeneralizedTime but WITHOUT the ASN.1 headers (class/tag/length) +func generalizedTimevalue(t time.Time) []byte { + dst := []byte{} + + year := t.Year() + if year < 0 || year > 9999 { + panic("cannot represent time as GeneralizedTime (invalid year)") + } + + dst = appendFourDigits(dst, year) + + _, month, day := t.Date() + + dst = appendTwoDigits(dst, int(month)) + dst = appendTwoDigits(dst, day) + + hour, min, sec := t.Clock() + + dst = appendTwoDigits(dst, hour) + dst = appendTwoDigits(dst, min) + dst = appendTwoDigits(dst, sec) + + _, offset := t.Zone() + + switch { + case offset/60 == 0: + return append(dst, 'Z') + case offset > 0: + dst = append(dst, '+') + case offset < 0: + dst = append(dst, '-') + } + + offsetMinutes := offset / 60 + if offsetMinutes < 0 { + offsetMinutes = -offsetMinutes + } + + dst = appendTwoDigits(dst, offsetMinutes/60) + dst = appendTwoDigits(dst, offsetMinutes%60) + + return dst +} + +// helper funcs from golang asn1 package - END + +// GeneralizedTimeExplicitValue returns t encoded as a GeneralizedTime, however +// instead of tagging it with GeneralizedTime it is instead tagged with an +// explicit tag of the specified tag number +func GeneralizedTimeExplicitValue(explicitTagNumber int, t time.Time) []byte { + return ExplicitValue(explicitTagNumber, generalizedTimevalue(t)) +} diff --git a/pkg/tools/asn1obj/integer.go b/pkg/tools/asn1obj/integer.go index 6aef433..fe849dc 100644 --- a/pkg/tools/asn1obj/integer.go +++ b/pkg/tools/asn1obj/integer.go @@ -15,3 +15,20 @@ func Integer(bigInt *big.Int) []byte { return asn1result } + +// IntegerExplicitValue returns bigInt encoded as an Integer, however +// instead of tagging it with Integer it is instead tagged with an +// explicit tag of the specified tag number +func IntegerExplicitValue(explicitTagNumber int, bigInt *big.Int) []byte { + intBytes := Integer(bigInt) + + asn1Obj := asn1.RawValue{} + rest, err := asn1.Unmarshal(intBytes, &asn1Obj) + if err != nil { + panic(err) + } else if len(rest) > 0 { + panic("invalid extra data") + } + + return ExplicitValue(explicitTagNumber, asn1Obj.Bytes) +} diff --git a/pkg/tools/asn1obj/misc.go b/pkg/tools/asn1obj/misc.go deleted file mode 100644 index 75bbc66..0000000 --- a/pkg/tools/asn1obj/misc.go +++ /dev/null @@ -1,27 +0,0 @@ -package asn1obj - -import "encoding/asn1" - -// Explicit wraps another ASN.1 Object with the EXPLICIT wrapper using -// the tag number specified -func Explicit(explicitTagNumber int, wrappedElement []byte) []byte { - raw := asn1.RawValue{ - Class: asn1.ClassContextSpecific, - Tag: explicitTagNumber, - IsCompound: true, - Bytes: wrappedElement, - } - - // should never error - asn1result, err := asn1.Marshal(raw) - if err != nil { - panic(err) - } - - return asn1result -} - -// Null returns the NULL value -func Null() []byte { - return asn1.NullBytes -} diff --git a/pkg/tools/asn1obj/oid.go b/pkg/tools/asn1obj/oid.go index 8327919..3975ded 100644 --- a/pkg/tools/asn1obj/oid.go +++ b/pkg/tools/asn1obj/oid.go @@ -3,8 +3,14 @@ package asn1obj import "encoding/asn1" var ( - OIDPkscs15Content = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 15, 3, 1} // pkcs15content (PKCS #15 content type) - OIDrsaEncryptionPKCS1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} // rsaEncryption (PKCS #1) + OIDPkscs15Content = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 15, 3, 1} // pkcs15content (PKCS #15 content type) + OIDrsaEncryptionPKCS1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1} // rsaEncryption (PKCS #1) + OIDpkcs5PBKDF2 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 5, 12} // pkcs5PBKDF2 (PKCS #5 v2.0) + OIDhmacWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 113549, 2, 9} // hmacWithSHA256 (RSADSI digestAlgorithm) + OIDpwriKEK = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 9} // pwriKEK (S/MIME Algorithms) + OIDdesEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7} // des-EDE3-CBC (RSADSI encryptionAlgorithm) + OIDpkcs7Data = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1} // data (PKCS #7) + OIDauthEnc128 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 16, 3, 15} // authEnc128 (S/MIME Algorithms) ) // ObjectIdentifier returns an ASN.1 OBJECT IDENTIFIER with the oidValue bytes diff --git a/pkg/tools/asn1obj/set.go b/pkg/tools/asn1obj/set.go new file mode 100644 index 0000000..58c003a --- /dev/null +++ b/pkg/tools/asn1obj/set.go @@ -0,0 +1,26 @@ +package asn1obj + +import "encoding/asn1" + +// Set returns an ASN.1 SET with the specified content +func Set(content [][]byte) []byte { + val := []byte{} + for i := range content { + val = append(val, content[i]...) + } + + raw := asn1.RawValue{ + Class: asn1.ClassUniversal, + Tag: asn1.TagSet, + IsCompound: true, + Bytes: val, + } + + // should never error + asn1result, err := asn1.Marshal(raw) + if err != nil { + panic(err) + } + + return asn1result +}