Merge pull request #3134 from stefanberger/encryption_code_plus_ctr.pr
Add image encryption support and ctr support
This commit is contained in:
22
vendor/github.com/fullsailor/pkcs7/LICENSE
generated
vendored
Normal file
22
vendor/github.com/fullsailor/pkcs7/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Andrew Smith
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
8
vendor/github.com/fullsailor/pkcs7/README.md
generated
vendored
Normal file
8
vendor/github.com/fullsailor/pkcs7/README.md
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# pkcs7
|
||||
|
||||
[](https://godoc.org/github.com/fullsailor/pkcs7)
|
||||
[](https://travis-ci.org/fullsailor/pkcs7)
|
||||
|
||||
pkcs7 implements parsing and creating signed and enveloped messages.
|
||||
|
||||
- Documentation on [GoDoc](http://godoc.org/github.com/fullsailor/pkcs7)
|
248
vendor/github.com/fullsailor/pkcs7/ber.go
generated
vendored
Normal file
248
vendor/github.com/fullsailor/pkcs7/ber.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var encodeIndent = 0
|
||||
|
||||
type asn1Object interface {
|
||||
EncodeTo(writer *bytes.Buffer) error
|
||||
}
|
||||
|
||||
type asn1Structured struct {
|
||||
tagBytes []byte
|
||||
content []asn1Object
|
||||
}
|
||||
|
||||
func (s asn1Structured) EncodeTo(out *bytes.Buffer) error {
|
||||
//fmt.Printf("%s--> tag: % X\n", strings.Repeat("| ", encodeIndent), s.tagBytes)
|
||||
encodeIndent++
|
||||
inner := new(bytes.Buffer)
|
||||
for _, obj := range s.content {
|
||||
err := obj.EncodeTo(inner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
encodeIndent--
|
||||
out.Write(s.tagBytes)
|
||||
encodeLength(out, inner.Len())
|
||||
out.Write(inner.Bytes())
|
||||
return nil
|
||||
}
|
||||
|
||||
type asn1Primitive struct {
|
||||
tagBytes []byte
|
||||
length int
|
||||
content []byte
|
||||
}
|
||||
|
||||
func (p asn1Primitive) EncodeTo(out *bytes.Buffer) error {
|
||||
_, err := out.Write(p.tagBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = encodeLength(out, p.length); err != nil {
|
||||
return err
|
||||
}
|
||||
//fmt.Printf("%s--> tag: % X length: %d\n", strings.Repeat("| ", encodeIndent), p.tagBytes, p.length)
|
||||
//fmt.Printf("%s--> content length: %d\n", strings.Repeat("| ", encodeIndent), len(p.content))
|
||||
out.Write(p.content)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ber2der(ber []byte) ([]byte, error) {
|
||||
if len(ber) == 0 {
|
||||
return nil, errors.New("ber2der: input ber is empty")
|
||||
}
|
||||
//fmt.Printf("--> ber2der: Transcoding %d bytes\n", len(ber))
|
||||
out := new(bytes.Buffer)
|
||||
|
||||
obj, _, err := readObject(ber, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj.EncodeTo(out)
|
||||
|
||||
// if offset < len(ber) {
|
||||
// return nil, fmt.Errorf("ber2der: Content longer than expected. Got %d, expected %d", offset, len(ber))
|
||||
//}
|
||||
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
// encodes lengths that are longer than 127 into string of bytes
|
||||
func marshalLongLength(out *bytes.Buffer, i int) (err error) {
|
||||
n := lengthLength(i)
|
||||
|
||||
for ; n > 0; n-- {
|
||||
err = out.WriteByte(byte(i >> uint((n-1)*8)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// computes the byte length of an encoded length value
|
||||
func lengthLength(i int) (numBytes int) {
|
||||
numBytes = 1
|
||||
for i > 255 {
|
||||
numBytes++
|
||||
i >>= 8
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// encodes the length in DER format
|
||||
// If the length fits in 7 bits, the value is encoded directly.
|
||||
//
|
||||
// Otherwise, the number of bytes to encode the length is first determined.
|
||||
// This number is likely to be 4 or less for a 32bit length. This number is
|
||||
// added to 0x80. The length is encoded in big endian encoding follow after
|
||||
//
|
||||
// Examples:
|
||||
// length | byte 1 | bytes n
|
||||
// 0 | 0x00 | -
|
||||
// 120 | 0x78 | -
|
||||
// 200 | 0x81 | 0xC8
|
||||
// 500 | 0x82 | 0x01 0xF4
|
||||
//
|
||||
func encodeLength(out *bytes.Buffer, length int) (err error) {
|
||||
if length >= 128 {
|
||||
l := lengthLength(length)
|
||||
err = out.WriteByte(0x80 | byte(l))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = marshalLongLength(out, length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = out.WriteByte(byte(length))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func readObject(ber []byte, offset int) (asn1Object, int, error) {
|
||||
//fmt.Printf("\n====> Starting readObject at offset: %d\n\n", offset)
|
||||
tagStart := offset
|
||||
b := ber[offset]
|
||||
offset++
|
||||
tag := b & 0x1F // last 5 bits
|
||||
if tag == 0x1F {
|
||||
tag = 0
|
||||
for ber[offset] >= 0x80 {
|
||||
tag = tag*128 + ber[offset] - 0x80
|
||||
offset++
|
||||
}
|
||||
tag = tag*128 + ber[offset] - 0x80
|
||||
offset++
|
||||
}
|
||||
tagEnd := offset
|
||||
|
||||
kind := b & 0x20
|
||||
/*
|
||||
if kind == 0 {
|
||||
fmt.Print("--> Primitive\n")
|
||||
} else {
|
||||
fmt.Print("--> Constructed\n")
|
||||
}
|
||||
*/
|
||||
// read length
|
||||
var length int
|
||||
l := ber[offset]
|
||||
offset++
|
||||
indefinite := false
|
||||
if l > 0x80 {
|
||||
numberOfBytes := (int)(l & 0x7F)
|
||||
if numberOfBytes > 4 { // int is only guaranteed to be 32bit
|
||||
return nil, 0, errors.New("ber2der: BER tag length too long")
|
||||
}
|
||||
if numberOfBytes == 4 && (int)(ber[offset]) > 0x7F {
|
||||
return nil, 0, errors.New("ber2der: BER tag length is negative")
|
||||
}
|
||||
if 0x0 == (int)(ber[offset]) {
|
||||
return nil, 0, errors.New("ber2der: BER tag length has leading zero")
|
||||
}
|
||||
//fmt.Printf("--> (compute length) indicator byte: %x\n", l)
|
||||
//fmt.Printf("--> (compute length) length bytes: % X\n", ber[offset:offset+numberOfBytes])
|
||||
for i := 0; i < numberOfBytes; i++ {
|
||||
length = length*256 + (int)(ber[offset])
|
||||
offset++
|
||||
}
|
||||
} else if l == 0x80 {
|
||||
indefinite = true
|
||||
} else {
|
||||
length = (int)(l)
|
||||
}
|
||||
|
||||
//fmt.Printf("--> length : %d\n", length)
|
||||
contentEnd := offset + length
|
||||
if contentEnd > len(ber) {
|
||||
return nil, 0, errors.New("ber2der: BER tag length is more than available data")
|
||||
}
|
||||
//fmt.Printf("--> content start : %d\n", offset)
|
||||
//fmt.Printf("--> content end : %d\n", contentEnd)
|
||||
//fmt.Printf("--> content : % X\n", ber[offset:contentEnd])
|
||||
var obj asn1Object
|
||||
if indefinite && kind == 0 {
|
||||
return nil, 0, errors.New("ber2der: Indefinite form tag must have constructed encoding")
|
||||
}
|
||||
if kind == 0 {
|
||||
obj = asn1Primitive{
|
||||
tagBytes: ber[tagStart:tagEnd],
|
||||
length: length,
|
||||
content: ber[offset:contentEnd],
|
||||
}
|
||||
} else {
|
||||
var subObjects []asn1Object
|
||||
for (offset < contentEnd) || indefinite {
|
||||
var subObj asn1Object
|
||||
var err error
|
||||
subObj, offset, err = readObject(ber, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
subObjects = append(subObjects, subObj)
|
||||
|
||||
if indefinite {
|
||||
terminated, err := isIndefiniteTermination(ber, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if terminated {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
obj = asn1Structured{
|
||||
tagBytes: ber[tagStart:tagEnd],
|
||||
content: subObjects,
|
||||
}
|
||||
}
|
||||
|
||||
// Apply indefinite form length with 0x0000 terminator.
|
||||
if indefinite {
|
||||
contentEnd = offset + 2
|
||||
}
|
||||
|
||||
return obj, contentEnd, nil
|
||||
}
|
||||
|
||||
func isIndefiniteTermination(ber []byte, offset int) (bool, error) {
|
||||
if len(ber) - offset < 2 {
|
||||
return false, errors.New("ber2der: Invalid BER format")
|
||||
}
|
||||
|
||||
return bytes.Index(ber[offset:], []byte{0x0, 0x0}) == 0, nil
|
||||
}
|
962
vendor/github.com/fullsailor/pkcs7/pkcs7.go
generated
vendored
Normal file
962
vendor/github.com/fullsailor/pkcs7/pkcs7.go
generated
vendored
Normal file
@@ -0,0 +1,962 @@
|
||||
// Package pkcs7 implements parsing and generation of some PKCS#7 structures.
|
||||
package pkcs7
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
_ "crypto/sha1" // for crypto.SHA1
|
||||
)
|
||||
|
||||
// PKCS7 Represents a PKCS7 structure
|
||||
type PKCS7 struct {
|
||||
Content []byte
|
||||
Certificates []*x509.Certificate
|
||||
CRLs []pkix.CertificateList
|
||||
Signers []signerInfo
|
||||
raw interface{}
|
||||
}
|
||||
|
||||
type contentInfo struct {
|
||||
ContentType asn1.ObjectIdentifier
|
||||
Content asn1.RawValue `asn1:"explicit,optional,tag:0"`
|
||||
}
|
||||
|
||||
// ErrUnsupportedContentType is returned when a PKCS7 content is not supported.
|
||||
// Currently only Data (1.2.840.113549.1.7.1), Signed Data (1.2.840.113549.1.7.2),
|
||||
// and Enveloped Data are supported (1.2.840.113549.1.7.3)
|
||||
var ErrUnsupportedContentType = errors.New("pkcs7: cannot parse data: unimplemented content type")
|
||||
|
||||
type unsignedData []byte
|
||||
|
||||
var (
|
||||
oidData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 1}
|
||||
oidSignedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 2}
|
||||
oidEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 3}
|
||||
oidSignedAndEnvelopedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 4}
|
||||
oidDigestedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 5}
|
||||
oidEncryptedData = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 7, 6}
|
||||
oidAttributeContentType = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 3}
|
||||
oidAttributeMessageDigest = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 4}
|
||||
oidAttributeSigningTime = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 5}
|
||||
)
|
||||
|
||||
type signedData struct {
|
||||
Version int `asn1:"default:1"`
|
||||
DigestAlgorithmIdentifiers []pkix.AlgorithmIdentifier `asn1:"set"`
|
||||
ContentInfo contentInfo
|
||||
Certificates rawCertificates `asn1:"optional,tag:0"`
|
||||
CRLs []pkix.CertificateList `asn1:"optional,tag:1"`
|
||||
SignerInfos []signerInfo `asn1:"set"`
|
||||
}
|
||||
|
||||
type rawCertificates struct {
|
||||
Raw asn1.RawContent
|
||||
}
|
||||
|
||||
type envelopedData struct {
|
||||
Version int
|
||||
RecipientInfos []recipientInfo `asn1:"set"`
|
||||
EncryptedContentInfo encryptedContentInfo
|
||||
}
|
||||
|
||||
type recipientInfo struct {
|
||||
Version int
|
||||
IssuerAndSerialNumber issuerAndSerial
|
||||
KeyEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedKey []byte
|
||||
}
|
||||
|
||||
type encryptedContentInfo struct {
|
||||
ContentType asn1.ObjectIdentifier
|
||||
ContentEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedContent asn1.RawValue `asn1:"tag:0,optional"`
|
||||
}
|
||||
|
||||
type attribute struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value asn1.RawValue `asn1:"set"`
|
||||
}
|
||||
|
||||
type issuerAndSerial struct {
|
||||
IssuerName asn1.RawValue
|
||||
SerialNumber *big.Int
|
||||
}
|
||||
|
||||
// MessageDigestMismatchError is returned when the signer data digest does not
|
||||
// match the computed digest for the contained content
|
||||
type MessageDigestMismatchError struct {
|
||||
ExpectedDigest []byte
|
||||
ActualDigest []byte
|
||||
}
|
||||
|
||||
func (err *MessageDigestMismatchError) Error() string {
|
||||
return fmt.Sprintf("pkcs7: Message digest mismatch\n\tExpected: %X\n\tActual : %X", err.ExpectedDigest, err.ActualDigest)
|
||||
}
|
||||
|
||||
type signerInfo struct {
|
||||
Version int `asn1:"default:1"`
|
||||
IssuerAndSerialNumber issuerAndSerial
|
||||
DigestAlgorithm pkix.AlgorithmIdentifier
|
||||
AuthenticatedAttributes []attribute `asn1:"optional,tag:0"`
|
||||
DigestEncryptionAlgorithm pkix.AlgorithmIdentifier
|
||||
EncryptedDigest []byte
|
||||
UnauthenticatedAttributes []attribute `asn1:"optional,tag:1"`
|
||||
}
|
||||
|
||||
// Parse decodes a DER encoded PKCS7 package
|
||||
func Parse(data []byte) (p7 *PKCS7, err error) {
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("pkcs7: input data is empty")
|
||||
}
|
||||
var info contentInfo
|
||||
der, err := ber2der(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rest, err := asn1.Unmarshal(der, &info)
|
||||
if len(rest) > 0 {
|
||||
err = asn1.SyntaxError{Msg: "trailing data"}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// fmt.Printf("--> Content Type: %s", info.ContentType)
|
||||
switch {
|
||||
case info.ContentType.Equal(oidSignedData):
|
||||
return parseSignedData(info.Content.Bytes)
|
||||
case info.ContentType.Equal(oidEnvelopedData):
|
||||
return parseEnvelopedData(info.Content.Bytes)
|
||||
}
|
||||
return nil, ErrUnsupportedContentType
|
||||
}
|
||||
|
||||
func parseSignedData(data []byte) (*PKCS7, error) {
|
||||
var sd signedData
|
||||
asn1.Unmarshal(data, &sd)
|
||||
certs, err := sd.Certificates.Parse()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// fmt.Printf("--> Signed Data Version %d\n", sd.Version)
|
||||
|
||||
var compound asn1.RawValue
|
||||
var content unsignedData
|
||||
|
||||
// The Content.Bytes maybe empty on PKI responses.
|
||||
if len(sd.ContentInfo.Content.Bytes) > 0 {
|
||||
if _, err := asn1.Unmarshal(sd.ContentInfo.Content.Bytes, &compound); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Compound octet string
|
||||
if compound.IsCompound {
|
||||
if _, err = asn1.Unmarshal(compound.Bytes, &content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// assuming this is tag 04
|
||||
content = compound.Bytes
|
||||
}
|
||||
return &PKCS7{
|
||||
Content: content,
|
||||
Certificates: certs,
|
||||
CRLs: sd.CRLs,
|
||||
Signers: sd.SignerInfos,
|
||||
raw: sd}, nil
|
||||
}
|
||||
|
||||
func (raw rawCertificates) Parse() ([]*x509.Certificate, error) {
|
||||
if len(raw.Raw) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var val asn1.RawValue
|
||||
if _, err := asn1.Unmarshal(raw.Raw, &val); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return x509.ParseCertificates(val.Bytes)
|
||||
}
|
||||
|
||||
func parseEnvelopedData(data []byte) (*PKCS7, error) {
|
||||
var ed envelopedData
|
||||
if _, err := asn1.Unmarshal(data, &ed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PKCS7{
|
||||
raw: ed,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Verify checks the signatures of a PKCS7 object
|
||||
// WARNING: Verify does not check signing time or verify certificate chains at
|
||||
// this time.
|
||||
func (p7 *PKCS7) Verify() (err error) {
|
||||
if len(p7.Signers) == 0 {
|
||||
return errors.New("pkcs7: Message has no signers")
|
||||
}
|
||||
for _, signer := range p7.Signers {
|
||||
if err := verifySignature(p7, signer); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifySignature(p7 *PKCS7, signer signerInfo) error {
|
||||
signedData := p7.Content
|
||||
hash, err := getHashForOID(signer.DigestAlgorithm.Algorithm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(signer.AuthenticatedAttributes) > 0 {
|
||||
// TODO(fullsailor): First check the content type match
|
||||
var digest []byte
|
||||
err := unmarshalAttribute(signer.AuthenticatedAttributes, oidAttributeMessageDigest, &digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write(p7.Content)
|
||||
computed := h.Sum(nil)
|
||||
if !hmac.Equal(digest, computed) {
|
||||
return &MessageDigestMismatchError{
|
||||
ExpectedDigest: digest,
|
||||
ActualDigest: computed,
|
||||
}
|
||||
}
|
||||
// TODO(fullsailor): Optionally verify certificate chain
|
||||
// TODO(fullsailor): Optionally verify signingTime against certificate NotAfter/NotBefore
|
||||
signedData, err = marshalAttributes(signer.AuthenticatedAttributes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cert := getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
||||
if cert == nil {
|
||||
return errors.New("pkcs7: No certificate for signer")
|
||||
}
|
||||
|
||||
algo := getSignatureAlgorithmFromAI(signer.DigestEncryptionAlgorithm)
|
||||
if algo == x509.UnknownSignatureAlgorithm {
|
||||
// I'm not sure what the spec here is, and the openssl sources were not
|
||||
// helpful. But, this is what App Store receipts appear to do.
|
||||
// The DigestEncryptionAlgorithm is just "rsaEncryption (PKCS #1)"
|
||||
// But we're expecting a digest + encryption algorithm. So... we're going
|
||||
// to determine an algorithm based on the DigestAlgorithm and this
|
||||
// encryption algorithm.
|
||||
if signer.DigestEncryptionAlgorithm.Algorithm.Equal(oidEncryptionAlgorithmRSA) {
|
||||
algo = getRSASignatureAlgorithmForDigestAlgorithm(hash)
|
||||
}
|
||||
}
|
||||
return cert.CheckSignature(algo, signedData, signer.EncryptedDigest)
|
||||
}
|
||||
|
||||
func marshalAttributes(attrs []attribute) ([]byte, error) {
|
||||
encodedAttributes, err := asn1.Marshal(struct {
|
||||
A []attribute `asn1:"set"`
|
||||
}{A: attrs})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Remove the leading sequence octets
|
||||
var raw asn1.RawValue
|
||||
asn1.Unmarshal(encodedAttributes, &raw)
|
||||
return raw.Bytes, nil
|
||||
}
|
||||
|
||||
var (
|
||||
oidDigestAlgorithmSHA1 = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 26}
|
||||
oidEncryptionAlgorithmRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 1}
|
||||
)
|
||||
|
||||
func getCertFromCertsByIssuerAndSerial(certs []*x509.Certificate, ias issuerAndSerial) *x509.Certificate {
|
||||
for _, cert := range certs {
|
||||
if isCertMatchForIssuerAndSerial(cert, ias) {
|
||||
return cert
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getHashForOID(oid asn1.ObjectIdentifier) (crypto.Hash, error) {
|
||||
switch {
|
||||
case oid.Equal(oidDigestAlgorithmSHA1):
|
||||
return crypto.SHA1, nil
|
||||
case oid.Equal(oidSHA256):
|
||||
return crypto.SHA256, nil
|
||||
}
|
||||
return crypto.Hash(0), ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
func getRSASignatureAlgorithmForDigestAlgorithm(hash crypto.Hash) x509.SignatureAlgorithm {
|
||||
for _, details := range signatureAlgorithmDetails {
|
||||
if details.pubKeyAlgo == x509.RSA && details.hash == hash {
|
||||
return details.algo
|
||||
}
|
||||
}
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
||||
|
||||
// GetOnlySigner returns an x509.Certificate for the first signer of the signed
|
||||
// data payload. If there are more or less than one signer, nil is returned
|
||||
func (p7 *PKCS7) GetOnlySigner() *x509.Certificate {
|
||||
if len(p7.Signers) != 1 {
|
||||
return nil
|
||||
}
|
||||
signer := p7.Signers[0]
|
||||
return getCertFromCertsByIssuerAndSerial(p7.Certificates, signer.IssuerAndSerialNumber)
|
||||
}
|
||||
|
||||
// ErrUnsupportedAlgorithm tells you when our quick dev assumptions have failed
|
||||
var ErrUnsupportedAlgorithm = errors.New("pkcs7: cannot decrypt data: only RSA, DES, DES-EDE3, AES-256-CBC and AES-128-GCM supported")
|
||||
|
||||
// ErrNotEncryptedContent is returned when attempting to Decrypt data that is not encrypted data
|
||||
var ErrNotEncryptedContent = errors.New("pkcs7: content data is a decryptable data type")
|
||||
|
||||
// Decrypt decrypts encrypted content info for recipient cert and private key
|
||||
func (p7 *PKCS7) Decrypt(cert *x509.Certificate, pk crypto.PrivateKey) ([]byte, error) {
|
||||
data, ok := p7.raw.(envelopedData)
|
||||
if !ok {
|
||||
return nil, ErrNotEncryptedContent
|
||||
}
|
||||
recipient := selectRecipientForCertificate(data.RecipientInfos, cert)
|
||||
if recipient.EncryptedKey == nil {
|
||||
return nil, errors.New("pkcs7: no enveloped recipient for provided certificate")
|
||||
}
|
||||
if priv := pk.(*rsa.PrivateKey); priv != nil {
|
||||
var contentKey []byte
|
||||
contentKey, err := rsa.DecryptPKCS1v15(rand.Reader, priv, recipient.EncryptedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data.EncryptedContentInfo.decrypt(contentKey)
|
||||
}
|
||||
fmt.Printf("Unsupported Private Key: %v\n", pk)
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
var oidEncryptionAlgorithmDESCBC = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 7}
|
||||
var oidEncryptionAlgorithmDESEDE3CBC = asn1.ObjectIdentifier{1, 2, 840, 113549, 3, 7}
|
||||
var oidEncryptionAlgorithmAES256CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 42}
|
||||
var oidEncryptionAlgorithmAES128GCM = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 6}
|
||||
var oidEncryptionAlgorithmAES128CBC = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 1, 2}
|
||||
|
||||
func (eci encryptedContentInfo) decrypt(key []byte) ([]byte, error) {
|
||||
alg := eci.ContentEncryptionAlgorithm.Algorithm
|
||||
if !alg.Equal(oidEncryptionAlgorithmDESCBC) &&
|
||||
!alg.Equal(oidEncryptionAlgorithmDESEDE3CBC) &&
|
||||
!alg.Equal(oidEncryptionAlgorithmAES256CBC) &&
|
||||
!alg.Equal(oidEncryptionAlgorithmAES128CBC) &&
|
||||
!alg.Equal(oidEncryptionAlgorithmAES128GCM) {
|
||||
fmt.Printf("Unsupported Content Encryption Algorithm: %s\n", alg)
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// EncryptedContent can either be constructed of multple OCTET STRINGs
|
||||
// or _be_ a tagged OCTET STRING
|
||||
var cyphertext []byte
|
||||
if eci.EncryptedContent.IsCompound {
|
||||
// Complex case to concat all of the children OCTET STRINGs
|
||||
var buf bytes.Buffer
|
||||
cypherbytes := eci.EncryptedContent.Bytes
|
||||
for {
|
||||
var part []byte
|
||||
cypherbytes, _ = asn1.Unmarshal(cypherbytes, &part)
|
||||
buf.Write(part)
|
||||
if cypherbytes == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
cyphertext = buf.Bytes()
|
||||
} else {
|
||||
// Simple case, the bytes _are_ the cyphertext
|
||||
cyphertext = eci.EncryptedContent.Bytes
|
||||
}
|
||||
|
||||
var block cipher.Block
|
||||
var err error
|
||||
|
||||
switch {
|
||||
case alg.Equal(oidEncryptionAlgorithmDESCBC):
|
||||
block, err = des.NewCipher(key)
|
||||
case alg.Equal(oidEncryptionAlgorithmDESEDE3CBC):
|
||||
block, err = des.NewTripleDESCipher(key)
|
||||
case alg.Equal(oidEncryptionAlgorithmAES256CBC):
|
||||
fallthrough
|
||||
case alg.Equal(oidEncryptionAlgorithmAES128GCM), alg.Equal(oidEncryptionAlgorithmAES128CBC):
|
||||
block, err = aes.NewCipher(key)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if alg.Equal(oidEncryptionAlgorithmAES128GCM) {
|
||||
params := aesGCMParameters{}
|
||||
paramBytes := eci.ContentEncryptionAlgorithm.Parameters.Bytes
|
||||
|
||||
_, err := asn1.Unmarshal(paramBytes, ¶ms)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(params.Nonce) != gcm.NonceSize() {
|
||||
return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
|
||||
}
|
||||
if params.ICVLen != gcm.Overhead() {
|
||||
return nil, errors.New("pkcs7: encryption algorithm parameters are incorrect")
|
||||
}
|
||||
|
||||
plaintext, err := gcm.Open(nil, params.Nonce, cyphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
iv := eci.ContentEncryptionAlgorithm.Parameters.Bytes
|
||||
if len(iv) != block.BlockSize() {
|
||||
return nil, errors.New("pkcs7: encryption algorithm parameters are malformed")
|
||||
}
|
||||
mode := cipher.NewCBCDecrypter(block, iv)
|
||||
plaintext := make([]byte, len(cyphertext))
|
||||
mode.CryptBlocks(plaintext, cyphertext)
|
||||
if plaintext, err = unpad(plaintext, mode.BlockSize()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func selectRecipientForCertificate(recipients []recipientInfo, cert *x509.Certificate) recipientInfo {
|
||||
for _, recp := range recipients {
|
||||
if isCertMatchForIssuerAndSerial(cert, recp.IssuerAndSerialNumber) {
|
||||
return recp
|
||||
}
|
||||
}
|
||||
return recipientInfo{}
|
||||
}
|
||||
|
||||
func isCertMatchForIssuerAndSerial(cert *x509.Certificate, ias issuerAndSerial) bool {
|
||||
return cert.SerialNumber.Cmp(ias.SerialNumber) == 0 && bytes.Compare(cert.RawIssuer, ias.IssuerName.FullBytes) == 0
|
||||
}
|
||||
|
||||
func pad(data []byte, blocklen int) ([]byte, error) {
|
||||
if blocklen < 1 {
|
||||
return nil, fmt.Errorf("invalid blocklen %d", blocklen)
|
||||
}
|
||||
padlen := blocklen - (len(data) % blocklen)
|
||||
if padlen == 0 {
|
||||
padlen = blocklen
|
||||
}
|
||||
pad := bytes.Repeat([]byte{byte(padlen)}, padlen)
|
||||
return append(data, pad...), nil
|
||||
}
|
||||
|
||||
func unpad(data []byte, blocklen int) ([]byte, error) {
|
||||
if blocklen < 1 {
|
||||
return nil, fmt.Errorf("invalid blocklen %d", blocklen)
|
||||
}
|
||||
if len(data)%blocklen != 0 || len(data) == 0 {
|
||||
return nil, fmt.Errorf("invalid data len %d", len(data))
|
||||
}
|
||||
|
||||
// the last byte is the length of padding
|
||||
padlen := int(data[len(data)-1])
|
||||
|
||||
// check padding integrity, all bytes should be the same
|
||||
pad := data[len(data)-padlen:]
|
||||
for _, padbyte := range pad {
|
||||
if padbyte != byte(padlen) {
|
||||
return nil, errors.New("invalid padding")
|
||||
}
|
||||
}
|
||||
|
||||
return data[:len(data)-padlen], nil
|
||||
}
|
||||
|
||||
func unmarshalAttribute(attrs []attribute, attributeType asn1.ObjectIdentifier, out interface{}) error {
|
||||
for _, attr := range attrs {
|
||||
if attr.Type.Equal(attributeType) {
|
||||
_, err := asn1.Unmarshal(attr.Value.Bytes, out)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return errors.New("pkcs7: attribute type not in attributes")
|
||||
}
|
||||
|
||||
// UnmarshalSignedAttribute decodes a single attribute from the signer info
|
||||
func (p7 *PKCS7) UnmarshalSignedAttribute(attributeType asn1.ObjectIdentifier, out interface{}) error {
|
||||
sd, ok := p7.raw.(signedData)
|
||||
if !ok {
|
||||
return errors.New("pkcs7: payload is not signedData content")
|
||||
}
|
||||
if len(sd.SignerInfos) < 1 {
|
||||
return errors.New("pkcs7: payload has no signers")
|
||||
}
|
||||
attributes := sd.SignerInfos[0].AuthenticatedAttributes
|
||||
return unmarshalAttribute(attributes, attributeType, out)
|
||||
}
|
||||
|
||||
// SignedData is an opaque data structure for creating signed data payloads
|
||||
type SignedData struct {
|
||||
sd signedData
|
||||
certs []*x509.Certificate
|
||||
messageDigest []byte
|
||||
}
|
||||
|
||||
// Attribute represents a key value pair attribute. Value must be marshalable byte
|
||||
// `encoding/asn1`
|
||||
type Attribute struct {
|
||||
Type asn1.ObjectIdentifier
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// SignerInfoConfig are optional values to include when adding a signer
|
||||
type SignerInfoConfig struct {
|
||||
ExtraSignedAttributes []Attribute
|
||||
}
|
||||
|
||||
// NewSignedData initializes a SignedData with content
|
||||
func NewSignedData(data []byte) (*SignedData, error) {
|
||||
content, err := asn1.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ci := contentInfo{
|
||||
ContentType: oidData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
|
||||
}
|
||||
digAlg := pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidDigestAlgorithmSHA1,
|
||||
}
|
||||
h := crypto.SHA1.New()
|
||||
h.Write(data)
|
||||
md := h.Sum(nil)
|
||||
sd := signedData{
|
||||
ContentInfo: ci,
|
||||
Version: 1,
|
||||
DigestAlgorithmIdentifiers: []pkix.AlgorithmIdentifier{digAlg},
|
||||
}
|
||||
return &SignedData{sd: sd, messageDigest: md}, nil
|
||||
}
|
||||
|
||||
type attributes struct {
|
||||
types []asn1.ObjectIdentifier
|
||||
values []interface{}
|
||||
}
|
||||
|
||||
// Add adds the attribute, maintaining insertion order
|
||||
func (attrs *attributes) Add(attrType asn1.ObjectIdentifier, value interface{}) {
|
||||
attrs.types = append(attrs.types, attrType)
|
||||
attrs.values = append(attrs.values, value)
|
||||
}
|
||||
|
||||
type sortableAttribute struct {
|
||||
SortKey []byte
|
||||
Attribute attribute
|
||||
}
|
||||
|
||||
type attributeSet []sortableAttribute
|
||||
|
||||
func (sa attributeSet) Len() int {
|
||||
return len(sa)
|
||||
}
|
||||
|
||||
func (sa attributeSet) Less(i, j int) bool {
|
||||
return bytes.Compare(sa[i].SortKey, sa[j].SortKey) < 0
|
||||
}
|
||||
|
||||
func (sa attributeSet) Swap(i, j int) {
|
||||
sa[i], sa[j] = sa[j], sa[i]
|
||||
}
|
||||
|
||||
func (sa attributeSet) Attributes() []attribute {
|
||||
attrs := make([]attribute, len(sa))
|
||||
for i, attr := range sa {
|
||||
attrs[i] = attr.Attribute
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (attrs *attributes) ForMarshaling() ([]attribute, error) {
|
||||
sortables := make(attributeSet, len(attrs.types))
|
||||
for i := range sortables {
|
||||
attrType := attrs.types[i]
|
||||
attrValue := attrs.values[i]
|
||||
asn1Value, err := asn1.Marshal(attrValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
attr := attribute{
|
||||
Type: attrType,
|
||||
Value: asn1.RawValue{Tag: 17, IsCompound: true, Bytes: asn1Value}, // 17 == SET tag
|
||||
}
|
||||
encoded, err := asn1.Marshal(attr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sortables[i] = sortableAttribute{
|
||||
SortKey: encoded,
|
||||
Attribute: attr,
|
||||
}
|
||||
}
|
||||
sort.Sort(sortables)
|
||||
return sortables.Attributes(), nil
|
||||
}
|
||||
|
||||
// AddSigner signs attributes about the content and adds certificate to payload
|
||||
func (sd *SignedData) AddSigner(cert *x509.Certificate, pkey crypto.PrivateKey, config SignerInfoConfig) error {
|
||||
attrs := &attributes{}
|
||||
attrs.Add(oidAttributeContentType, sd.sd.ContentInfo.ContentType)
|
||||
attrs.Add(oidAttributeMessageDigest, sd.messageDigest)
|
||||
attrs.Add(oidAttributeSigningTime, time.Now())
|
||||
for _, attr := range config.ExtraSignedAttributes {
|
||||
attrs.Add(attr.Type, attr.Value)
|
||||
}
|
||||
finalAttrs, err := attrs.ForMarshaling()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signature, err := signAttributes(finalAttrs, pkey, crypto.SHA1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ias, err := cert2issuerAndSerial(cert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
signer := signerInfo{
|
||||
AuthenticatedAttributes: finalAttrs,
|
||||
DigestAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidDigestAlgorithmSHA1},
|
||||
DigestEncryptionAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSignatureSHA1WithRSA},
|
||||
IssuerAndSerialNumber: ias,
|
||||
EncryptedDigest: signature,
|
||||
Version: 1,
|
||||
}
|
||||
// create signature of signed attributes
|
||||
sd.certs = append(sd.certs, cert)
|
||||
sd.sd.SignerInfos = append(sd.sd.SignerInfos, signer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddCertificate adds the certificate to the payload. Useful for parent certificates
|
||||
func (sd *SignedData) AddCertificate(cert *x509.Certificate) {
|
||||
sd.certs = append(sd.certs, cert)
|
||||
}
|
||||
|
||||
// Detach removes content from the signed data struct to make it a detached signature.
|
||||
// This must be called right before Finish()
|
||||
func (sd *SignedData) Detach() {
|
||||
sd.sd.ContentInfo = contentInfo{ContentType: oidData}
|
||||
}
|
||||
|
||||
// Finish marshals the content and its signers
|
||||
func (sd *SignedData) Finish() ([]byte, error) {
|
||||
sd.sd.Certificates = marshalCertificates(sd.certs)
|
||||
inner, err := asn1.Marshal(sd.sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
outer := contentInfo{
|
||||
ContentType: oidSignedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: inner, IsCompound: true},
|
||||
}
|
||||
return asn1.Marshal(outer)
|
||||
}
|
||||
|
||||
func cert2issuerAndSerial(cert *x509.Certificate) (issuerAndSerial, error) {
|
||||
var ias issuerAndSerial
|
||||
// The issuer RDNSequence has to match exactly the sequence in the certificate
|
||||
// We cannot use cert.Issuer.ToRDNSequence() here since it mangles the sequence
|
||||
ias.IssuerName = asn1.RawValue{FullBytes: cert.RawIssuer}
|
||||
ias.SerialNumber = cert.SerialNumber
|
||||
|
||||
return ias, nil
|
||||
}
|
||||
|
||||
// signs the DER encoded form of the attributes with the private key
|
||||
func signAttributes(attrs []attribute, pkey crypto.PrivateKey, hash crypto.Hash) ([]byte, error) {
|
||||
attrBytes, err := marshalAttributes(attrs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h := hash.New()
|
||||
h.Write(attrBytes)
|
||||
hashed := h.Sum(nil)
|
||||
switch priv := pkey.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA1, hashed)
|
||||
}
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
// concats and wraps the certificates in the RawValue structure
|
||||
func marshalCertificates(certs []*x509.Certificate) rawCertificates {
|
||||
var buf bytes.Buffer
|
||||
for _, cert := range certs {
|
||||
buf.Write(cert.Raw)
|
||||
}
|
||||
rawCerts, _ := marshalCertificateBytes(buf.Bytes())
|
||||
return rawCerts
|
||||
}
|
||||
|
||||
// Even though, the tag & length are stripped out during marshalling the
|
||||
// RawContent, we have to encode it into the RawContent. If its missing,
|
||||
// then `asn1.Marshal()` will strip out the certificate wrapper instead.
|
||||
func marshalCertificateBytes(certs []byte) (rawCertificates, error) {
|
||||
var val = asn1.RawValue{Bytes: certs, Class: 2, Tag: 0, IsCompound: true}
|
||||
b, err := asn1.Marshal(val)
|
||||
if err != nil {
|
||||
return rawCertificates{}, err
|
||||
}
|
||||
return rawCertificates{Raw: b}, nil
|
||||
}
|
||||
|
||||
// DegenerateCertificate creates a signed data structure containing only the
|
||||
// provided certificate or certificate chain.
|
||||
func DegenerateCertificate(cert []byte) ([]byte, error) {
|
||||
rawCert, err := marshalCertificateBytes(cert)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
emptyContent := contentInfo{ContentType: oidData}
|
||||
sd := signedData{
|
||||
Version: 1,
|
||||
ContentInfo: emptyContent,
|
||||
Certificates: rawCert,
|
||||
CRLs: []pkix.CertificateList{},
|
||||
}
|
||||
content, err := asn1.Marshal(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signedContent := contentInfo{
|
||||
ContentType: oidSignedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, Bytes: content, IsCompound: true},
|
||||
}
|
||||
return asn1.Marshal(signedContent)
|
||||
}
|
||||
|
||||
const (
|
||||
EncryptionAlgorithmDESCBC = iota
|
||||
EncryptionAlgorithmAES128GCM
|
||||
)
|
||||
|
||||
// ContentEncryptionAlgorithm determines the algorithm used to encrypt the
|
||||
// plaintext message. Change the value of this variable to change which
|
||||
// algorithm is used in the Encrypt() function.
|
||||
var ContentEncryptionAlgorithm = EncryptionAlgorithmDESCBC
|
||||
|
||||
// ErrUnsupportedEncryptionAlgorithm is returned when attempting to encrypt
|
||||
// content with an unsupported algorithm.
|
||||
var ErrUnsupportedEncryptionAlgorithm = errors.New("pkcs7: cannot encrypt content: only DES-CBC and AES-128-GCM supported")
|
||||
|
||||
const nonceSize = 12
|
||||
|
||||
type aesGCMParameters struct {
|
||||
Nonce []byte `asn1:"tag:4"`
|
||||
ICVLen int
|
||||
}
|
||||
|
||||
func encryptAES128GCM(content []byte) ([]byte, *encryptedContentInfo, error) {
|
||||
// Create AES key and nonce
|
||||
key := make([]byte, 16)
|
||||
nonce := make([]byte, nonceSize)
|
||||
|
||||
_, err := rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
_, err = rand.Read(nonce)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Encrypt content
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ciphertext := gcm.Seal(nil, nonce, content, nil)
|
||||
|
||||
// Prepare ASN.1 Encrypted Content Info
|
||||
paramSeq := aesGCMParameters{
|
||||
Nonce: nonce,
|
||||
ICVLen: gcm.Overhead(),
|
||||
}
|
||||
|
||||
paramBytes, err := asn1.Marshal(paramSeq)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
eci := encryptedContentInfo{
|
||||
ContentType: oidData,
|
||||
ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidEncryptionAlgorithmAES128GCM,
|
||||
Parameters: asn1.RawValue{
|
||||
Tag: asn1.TagSequence,
|
||||
Bytes: paramBytes,
|
||||
},
|
||||
},
|
||||
EncryptedContent: marshalEncryptedContent(ciphertext),
|
||||
}
|
||||
|
||||
return key, &eci, nil
|
||||
}
|
||||
|
||||
func encryptDESCBC(content []byte) ([]byte, *encryptedContentInfo, error) {
|
||||
// Create DES key & CBC IV
|
||||
key := make([]byte, 8)
|
||||
iv := make([]byte, des.BlockSize)
|
||||
_, err := rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
_, err = rand.Read(iv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Encrypt padded content
|
||||
block, err := des.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
plaintext, err := pad(content, mode.BlockSize())
|
||||
cyphertext := make([]byte, len(plaintext))
|
||||
mode.CryptBlocks(cyphertext, plaintext)
|
||||
|
||||
// Prepare ASN.1 Encrypted Content Info
|
||||
eci := encryptedContentInfo{
|
||||
ContentType: oidData,
|
||||
ContentEncryptionAlgorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidEncryptionAlgorithmDESCBC,
|
||||
Parameters: asn1.RawValue{Tag: 4, Bytes: iv},
|
||||
},
|
||||
EncryptedContent: marshalEncryptedContent(cyphertext),
|
||||
}
|
||||
|
||||
return key, &eci, nil
|
||||
}
|
||||
|
||||
// Encrypt creates and returns an envelope data PKCS7 structure with encrypted
|
||||
// recipient keys for each recipient public key.
|
||||
//
|
||||
// The algorithm used to perform encryption is determined by the current value
|
||||
// of the global ContentEncryptionAlgorithm package variable. By default, the
|
||||
// value is EncryptionAlgorithmDESCBC. To use a different algorithm, change the
|
||||
// value before calling Encrypt(). For example:
|
||||
//
|
||||
// ContentEncryptionAlgorithm = EncryptionAlgorithmAES128GCM
|
||||
//
|
||||
// TODO(fullsailor): Add support for encrypting content with other algorithms
|
||||
func Encrypt(content []byte, recipients []*x509.Certificate) ([]byte, error) {
|
||||
var eci *encryptedContentInfo
|
||||
var key []byte
|
||||
var err error
|
||||
|
||||
// Apply chosen symmetric encryption method
|
||||
switch ContentEncryptionAlgorithm {
|
||||
case EncryptionAlgorithmDESCBC:
|
||||
key, eci, err = encryptDESCBC(content)
|
||||
|
||||
case EncryptionAlgorithmAES128GCM:
|
||||
key, eci, err = encryptAES128GCM(content)
|
||||
|
||||
default:
|
||||
return nil, ErrUnsupportedEncryptionAlgorithm
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepare each recipient's encrypted cipher key
|
||||
recipientInfos := make([]recipientInfo, len(recipients))
|
||||
for i, recipient := range recipients {
|
||||
encrypted, err := encryptKey(key, recipient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ias, err := cert2issuerAndSerial(recipient)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info := recipientInfo{
|
||||
Version: 0,
|
||||
IssuerAndSerialNumber: ias,
|
||||
KeyEncryptionAlgorithm: pkix.AlgorithmIdentifier{
|
||||
Algorithm: oidEncryptionAlgorithmRSA,
|
||||
},
|
||||
EncryptedKey: encrypted,
|
||||
}
|
||||
recipientInfos[i] = info
|
||||
}
|
||||
|
||||
// Prepare envelope content
|
||||
envelope := envelopedData{
|
||||
EncryptedContentInfo: *eci,
|
||||
Version: 0,
|
||||
RecipientInfos: recipientInfos,
|
||||
}
|
||||
innerContent, err := asn1.Marshal(envelope)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepare outer payload structure
|
||||
wrapper := contentInfo{
|
||||
ContentType: oidEnvelopedData,
|
||||
Content: asn1.RawValue{Class: 2, Tag: 0, IsCompound: true, Bytes: innerContent},
|
||||
}
|
||||
|
||||
return asn1.Marshal(wrapper)
|
||||
}
|
||||
|
||||
func marshalEncryptedContent(content []byte) asn1.RawValue {
|
||||
asn1Content, _ := asn1.Marshal(content)
|
||||
return asn1.RawValue{Tag: 0, Class: 2, Bytes: asn1Content, IsCompound: true}
|
||||
}
|
||||
|
||||
func encryptKey(key []byte, recipient *x509.Certificate) ([]byte, error) {
|
||||
if pub := recipient.PublicKey.(*rsa.PublicKey); pub != nil {
|
||||
return rsa.EncryptPKCS1v15(rand.Reader, pub, key)
|
||||
}
|
||||
return nil, ErrUnsupportedAlgorithm
|
||||
}
|
133
vendor/github.com/fullsailor/pkcs7/x509.go
generated
vendored
Normal file
133
vendor/github.com/fullsailor/pkcs7/x509.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the go/golang LICENSE file.
|
||||
|
||||
package pkcs7
|
||||
|
||||
// These are private constants and functions from the crypto/x509 package that
|
||||
// are useful when dealing with signatures verified by x509 certificates
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
)
|
||||
|
||||
var (
|
||||
oidSignatureMD2WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 2}
|
||||
oidSignatureMD5WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 4}
|
||||
oidSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 5}
|
||||
oidSignatureSHA256WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 11}
|
||||
oidSignatureSHA384WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 12}
|
||||
oidSignatureSHA512WithRSA = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 13}
|
||||
oidSignatureRSAPSS = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 10}
|
||||
oidSignatureDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10040, 4, 3}
|
||||
oidSignatureDSAWithSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 3, 2}
|
||||
oidSignatureECDSAWithSHA1 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 1}
|
||||
oidSignatureECDSAWithSHA256 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 2}
|
||||
oidSignatureECDSAWithSHA384 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 3}
|
||||
oidSignatureECDSAWithSHA512 = asn1.ObjectIdentifier{1, 2, 840, 10045, 4, 3, 4}
|
||||
|
||||
oidSHA256 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 1}
|
||||
oidSHA384 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 2}
|
||||
oidSHA512 = asn1.ObjectIdentifier{2, 16, 840, 1, 101, 3, 4, 2, 3}
|
||||
|
||||
oidMGF1 = asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 1, 8}
|
||||
|
||||
// oidISOSignatureSHA1WithRSA means the same as oidSignatureSHA1WithRSA
|
||||
// but it's specified by ISO. Microsoft's makecert.exe has been known
|
||||
// to produce certificates with this OID.
|
||||
oidISOSignatureSHA1WithRSA = asn1.ObjectIdentifier{1, 3, 14, 3, 2, 29}
|
||||
)
|
||||
|
||||
var signatureAlgorithmDetails = []struct {
|
||||
algo x509.SignatureAlgorithm
|
||||
name string
|
||||
oid asn1.ObjectIdentifier
|
||||
pubKeyAlgo x509.PublicKeyAlgorithm
|
||||
hash crypto.Hash
|
||||
}{
|
||||
{x509.MD2WithRSA, "MD2-RSA", oidSignatureMD2WithRSA, x509.RSA, crypto.Hash(0) /* no value for MD2 */},
|
||||
{x509.MD5WithRSA, "MD5-RSA", oidSignatureMD5WithRSA, x509.RSA, crypto.MD5},
|
||||
{x509.SHA1WithRSA, "SHA1-RSA", oidSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
|
||||
{x509.SHA1WithRSA, "SHA1-RSA", oidISOSignatureSHA1WithRSA, x509.RSA, crypto.SHA1},
|
||||
{x509.SHA256WithRSA, "SHA256-RSA", oidSignatureSHA256WithRSA, x509.RSA, crypto.SHA256},
|
||||
{x509.SHA384WithRSA, "SHA384-RSA", oidSignatureSHA384WithRSA, x509.RSA, crypto.SHA384},
|
||||
{x509.SHA512WithRSA, "SHA512-RSA", oidSignatureSHA512WithRSA, x509.RSA, crypto.SHA512},
|
||||
{x509.SHA256WithRSAPSS, "SHA256-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA256},
|
||||
{x509.SHA384WithRSAPSS, "SHA384-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA384},
|
||||
{x509.SHA512WithRSAPSS, "SHA512-RSAPSS", oidSignatureRSAPSS, x509.RSA, crypto.SHA512},
|
||||
{x509.DSAWithSHA1, "DSA-SHA1", oidSignatureDSAWithSHA1, x509.DSA, crypto.SHA1},
|
||||
{x509.DSAWithSHA256, "DSA-SHA256", oidSignatureDSAWithSHA256, x509.DSA, crypto.SHA256},
|
||||
{x509.ECDSAWithSHA1, "ECDSA-SHA1", oidSignatureECDSAWithSHA1, x509.ECDSA, crypto.SHA1},
|
||||
{x509.ECDSAWithSHA256, "ECDSA-SHA256", oidSignatureECDSAWithSHA256, x509.ECDSA, crypto.SHA256},
|
||||
{x509.ECDSAWithSHA384, "ECDSA-SHA384", oidSignatureECDSAWithSHA384, x509.ECDSA, crypto.SHA384},
|
||||
{x509.ECDSAWithSHA512, "ECDSA-SHA512", oidSignatureECDSAWithSHA512, x509.ECDSA, crypto.SHA512},
|
||||
}
|
||||
|
||||
// pssParameters reflects the parameters in an AlgorithmIdentifier that
|
||||
// specifies RSA PSS. See https://tools.ietf.org/html/rfc3447#appendix-A.2.3
|
||||
type pssParameters struct {
|
||||
// The following three fields are not marked as
|
||||
// optional because the default values specify SHA-1,
|
||||
// which is no longer suitable for use in signatures.
|
||||
Hash pkix.AlgorithmIdentifier `asn1:"explicit,tag:0"`
|
||||
MGF pkix.AlgorithmIdentifier `asn1:"explicit,tag:1"`
|
||||
SaltLength int `asn1:"explicit,tag:2"`
|
||||
TrailerField int `asn1:"optional,explicit,tag:3,default:1"`
|
||||
}
|
||||
|
||||
// asn1.NullBytes is not available prior to Go 1.9
|
||||
var nullBytes = []byte{5, 0}
|
||||
|
||||
func getSignatureAlgorithmFromAI(ai pkix.AlgorithmIdentifier) x509.SignatureAlgorithm {
|
||||
if !ai.Algorithm.Equal(oidSignatureRSAPSS) {
|
||||
for _, details := range signatureAlgorithmDetails {
|
||||
if ai.Algorithm.Equal(details.oid) {
|
||||
return details.algo
|
||||
}
|
||||
}
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
||||
|
||||
// RSA PSS is special because it encodes important parameters
|
||||
// in the Parameters.
|
||||
|
||||
var params pssParameters
|
||||
if _, err := asn1.Unmarshal(ai.Parameters.FullBytes, ¶ms); err != nil {
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
||||
|
||||
var mgf1HashFunc pkix.AlgorithmIdentifier
|
||||
if _, err := asn1.Unmarshal(params.MGF.Parameters.FullBytes, &mgf1HashFunc); err != nil {
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
||||
|
||||
// PSS is greatly overburdened with options. This code forces
|
||||
// them into three buckets by requiring that the MGF1 hash
|
||||
// function always match the message hash function (as
|
||||
// recommended in
|
||||
// https://tools.ietf.org/html/rfc3447#section-8.1), that the
|
||||
// salt length matches the hash length, and that the trailer
|
||||
// field has the default value.
|
||||
if !bytes.Equal(params.Hash.Parameters.FullBytes, nullBytes) ||
|
||||
!params.MGF.Algorithm.Equal(oidMGF1) ||
|
||||
!mgf1HashFunc.Algorithm.Equal(params.Hash.Algorithm) ||
|
||||
!bytes.Equal(mgf1HashFunc.Parameters.FullBytes, nullBytes) ||
|
||||
params.TrailerField != 1 {
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
||||
|
||||
switch {
|
||||
case params.Hash.Algorithm.Equal(oidSHA256) && params.SaltLength == 32:
|
||||
return x509.SHA256WithRSAPSS
|
||||
case params.Hash.Algorithm.Equal(oidSHA384) && params.SaltLength == 48:
|
||||
return x509.SHA384WithRSAPSS
|
||||
case params.Hash.Algorithm.Equal(oidSHA512) && params.SaltLength == 64:
|
||||
return x509.SHA512WithRSAPSS
|
||||
}
|
||||
|
||||
return x509.UnknownSignatureAlgorithm
|
||||
}
|
25
vendor/github.com/miscreant/miscreant-go/LICENSE.txt
generated
vendored
Normal file
25
vendor/github.com/miscreant/miscreant-go/LICENSE.txt
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2017-2018 The Miscreant Developers. The canonical list of project
|
||||
contributors who hold copyright over the project can be found at:
|
||||
|
||||
https://github.com/miscreant/miscreant/blob/master/AUTHORS.md
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
95
vendor/github.com/miscreant/miscreant-go/README.md
generated
vendored
Normal file
95
vendor/github.com/miscreant/miscreant-go/README.md
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
# miscreant.go [![Build Status][build-shield]][build-link] [![GoDoc][godoc-shield]][godoc-link] [![Go Report Card][goreport-shield]][goreport-link] [![MIT licensed][license-shield]][license-link] [![Gitter Chat][gitter-image]][gitter-link]
|
||||
|
||||
> The best crypto you've never heard of, brought to you by [Phil Rogaway]
|
||||
|
||||
Go implementation of **Miscreant**: Advanced symmetric encryption library
|
||||
which provides the [AES-SIV] ([RFC 5297]), [AES-PMAC-SIV], and [STREAM]
|
||||
constructions. These algorithms are easy-to-use (or rather, hard-to-misuse)
|
||||
and support encryption of individual messages or message streams.
|
||||
|
||||
```go
|
||||
import "github.com/miscreant/miscreant-go"
|
||||
```
|
||||
|
||||
All types are designed to be **thread-compatible**: Methods of an instance shared between
|
||||
multiple threads (or goroutines) must not be accessed concurrently. Callers are responsible for
|
||||
implementing their own mutual exclusion.
|
||||
|
||||
|
||||
- [Documentation] (Wiki)
|
||||
- [godoc][godoc-link]
|
||||
|
||||
## About AES-SIV and AES-PMAC-SIV
|
||||
|
||||
**AES-SIV** and **AES-PMAC-SIV** provide [nonce-reuse misuse-resistance] (NRMR):
|
||||
accidentally reusing a nonce with this construction is not a security
|
||||
catastrophe, unlike more popular AES encryption modes like [AES-GCM] where
|
||||
nonce reuse leaks both the authentication key and the XOR of both plaintexts,
|
||||
both of which can potentially be leveraged for *full plaintext recovery attacks*.
|
||||
|
||||
With **AES-SIV**, the worst outcome of reusing a nonce is an attacker
|
||||
can see you've sent the same plaintext twice, as opposed to almost all other
|
||||
AES modes where it can facilitate [chosen ciphertext attacks] and/or
|
||||
full plaintext recovery.
|
||||
|
||||
## Help and Discussion
|
||||
|
||||
Have questions? Want to suggest a feature or change?
|
||||
|
||||
* [Gitter]: web-based chat about miscreant projects including **miscreant.go**
|
||||
* [Google Group]: join via web or email ([miscreant-crypto+subscribe@googlegroups.com])
|
||||
|
||||
## Security Notice
|
||||
|
||||
Though this library is written by cryptographic professionals, it has not
|
||||
undergone a thorough security audit, and cryptographic professionals are still
|
||||
humans that make mistakes.
|
||||
|
||||
This library makes an effort to use constant time operations throughout its
|
||||
implementation, however actual constant time behavior has not been verified.
|
||||
|
||||
Use this library at your own risk.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
We abide by the [Contributor Covenant][cc] and ask that you do as well.
|
||||
|
||||
For more information, please see [CODE_OF_CONDUCT.md].
|
||||
|
||||
## Contributing
|
||||
|
||||
Bug reports and pull requests are welcome on GitHub at:
|
||||
|
||||
<https://github.com/miscreant/miscreant-go>
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright (c) 2017-2018 [The Miscreant Developers][AUTHORS].
|
||||
See [LICENSE.txt] for further details.
|
||||
|
||||
[build-shield]: https://secure.travis-ci.org/miscreant/miscreant-go.svg?branch=master
|
||||
[build-link]: https://travis-ci.org/miscreant/miscreant-go
|
||||
[godoc-shield]: https://godoc.org/github.com/miscreant/miscreant-go?status.svg
|
||||
[godoc-link]: https://godoc.org/github.com/miscreant/miscreant-go
|
||||
[goreport-shield]: https://goreportcard.com/badge/github.com/miscreant/miscreant-go
|
||||
[goreport-link]: https://goreportcard.com/report/github.com/miscreant/miscreant-go
|
||||
[license-shield]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[license-link]: https://github.com/miscreant/miscreant-go/blob/master/LICENSE.txt
|
||||
[gitter-image]: https://badges.gitter.im/badge.svg
|
||||
[gitter-link]: https://gitter.im/miscreant/Lobby
|
||||
[Phil Rogaway]: https://en.wikipedia.org/wiki/Phillip_Rogaway
|
||||
[AES-SIV]: https://github.com/miscreant/miscreant/wiki/AES-SIV
|
||||
[RFC 5297]: https://tools.ietf.org/html/rfc5297
|
||||
[AES-PMAC-SIV]: https://github.com/miscreant/miscreant/wiki/AES-PMAC-SIV
|
||||
[STREAM]: https://github.com/miscreant/miscreant/wiki/STREAM
|
||||
[nonce-reuse misuse-resistance]: https://github.com/miscreant/miscreant/wiki/Nonce-Reuse-Misuse-Resistance
|
||||
[AES-GCM]: https://en.wikipedia.org/wiki/Galois/Counter_Mode
|
||||
[chosen ciphertext attacks]: https://en.wikipedia.org/wiki/Chosen-ciphertext_attack
|
||||
[Documentation]: https://github.com/miscreant/miscreant/wiki/Go-Documentation
|
||||
[Gitter]: https://gitter.im/miscreant/Lobby
|
||||
[Google Group]: https://groups.google.com/forum/#!forum/miscreant-crypto
|
||||
[miscreant-crypto+subscribe@googlegroups.com]: mailto:miscreant-crypto+subscribe@googlegroups.com?subject=subscribe
|
||||
[cc]: https://contributor-covenant.org
|
||||
[CODE_OF_CONDUCT.md]: https://github.com/miscreant/miscreant-go/blob/master/CODE_OF_CONDUCT.md
|
||||
[AUTHORS]: https://github.com/miscreant/miscreant-go/blob/master/AUTHORS.md
|
||||
[LICENSE.txt]: https://github.com/miscreant/miscreant-go/blob/master/LICENSE.txt
|
108
vendor/github.com/miscreant/miscreant-go/aead.go
generated
vendored
Normal file
108
vendor/github.com/miscreant/miscreant-go/aead.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Written in 2015 by Dmitry Chestnykh.
|
||||
|
||||
package miscreant
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Minimum nonce size for which we'll allow the generation of random nonces
|
||||
const minimumRandomNonceSize = 16
|
||||
|
||||
// aead is a wrapper for Cipher implementing cipher.AEAD interface.
|
||||
type aead struct {
|
||||
// miscreant.Cipher instance underlying this AEAD
|
||||
c *Cipher
|
||||
|
||||
// Size of the nonce required
|
||||
nonceSize int
|
||||
}
|
||||
|
||||
// GenerateKey generates a random 32-byte or 64-byte encryption key.
|
||||
// Panics if the key size is unsupported or source of randomness fails.
|
||||
func GenerateKey(length int) []byte {
|
||||
if length != 32 && length != 64 {
|
||||
panic("miscreant.GenerateKey: invalid key size: " + string(length))
|
||||
}
|
||||
|
||||
key := make([]byte, length)
|
||||
_, err := io.ReadFull(rand.Reader, key[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
// GenerateNonce generates a random nonce for the given `cipher.AEAD`.
|
||||
// Panics if the configured nonce size is less than 16-bytes (128-bits)
|
||||
func GenerateNonce(c cipher.AEAD) []byte {
|
||||
if c.NonceSize() < minimumRandomNonceSize {
|
||||
panic("miscreant.GenerateNonce: nonce size is too small: " + string(c.NonceSize()))
|
||||
}
|
||||
|
||||
nonce := make([]byte, c.NonceSize())
|
||||
_, err := io.ReadFull(rand.Reader, nonce[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return nonce
|
||||
}
|
||||
|
||||
// NewAEAD returns an AES-SIV instance implementing cipher.AEAD interface,
|
||||
// with the given cipher, nonce size, and a key which must be twice as long
|
||||
// as an AES key, either 32 or 64 bytes to select AES-128 (AES-SIV-256)
|
||||
// or AES-256 (AES-SIV-512).
|
||||
//
|
||||
// Unless the given nonce size is less than zero, Seal and Open will panic when
|
||||
// passed nonce of a different size.
|
||||
func NewAEAD(alg string, key []byte, nonceSize int) (cipher.AEAD, error) {
|
||||
switch alg {
|
||||
case "AES-SIV", "AES-CMAC-SIV":
|
||||
c, err := NewAESCMACSIV(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &aead{c: c, nonceSize: nonceSize}, nil
|
||||
case "AES-PMAC-SIV":
|
||||
c, err := NewAESPMACSIV(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &aead{c: c, nonceSize: nonceSize}, nil
|
||||
default:
|
||||
panic("NewAEAD: unknown cipher: " + alg)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *aead) NonceSize() int { return a.nonceSize }
|
||||
func (a *aead) Overhead() int { return a.c.Overhead() }
|
||||
|
||||
func (a *aead) Seal(dst, nonce, plaintext, data []byte) (out []byte) {
|
||||
if len(nonce) != a.nonceSize && a.nonceSize >= 0 {
|
||||
panic("miscreant.AEAD: incorrect nonce length")
|
||||
}
|
||||
var err error
|
||||
if data == nil {
|
||||
out, err = a.c.Seal(dst, plaintext, nonce)
|
||||
} else {
|
||||
out, err = a.c.Seal(dst, plaintext, data, nonce)
|
||||
}
|
||||
if err != nil {
|
||||
panic("miscreant.AEAD: " + err.Error())
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (a *aead) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
|
||||
if len(nonce) != a.nonceSize && a.nonceSize >= 0 {
|
||||
panic("miscreant.AEAD: incorrect nonce length")
|
||||
}
|
||||
if data == nil {
|
||||
return a.c.Open(dst, ciphertext, nonce)
|
||||
}
|
||||
return a.c.Open(dst, ciphertext, data, nonce)
|
||||
}
|
49
vendor/github.com/miscreant/miscreant-go/block/block.go
generated
vendored
Normal file
49
vendor/github.com/miscreant/miscreant-go/block/block.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// Common block cipher functionality shared across this library
|
||||
|
||||
package block
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/subtle"
|
||||
)
|
||||
|
||||
const (
|
||||
// Size of an AES block in bytes
|
||||
Size = 16
|
||||
|
||||
// R is the minimal irreducible polynomial for a 128-bit block size
|
||||
R = 0x87
|
||||
)
|
||||
|
||||
// Block is a 128-bit array used by certain block ciphers (i.e. AES)
|
||||
type Block [Size]byte
|
||||
|
||||
// Clear zeroes out the contents of the block
|
||||
func (b *Block) Clear() {
|
||||
// TODO: use a more secure zeroing method that won't be optimized away
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Dbl performs a doubling of a block over GF(2^128):
|
||||
//
|
||||
// a<<1 if firstbit(a)=0
|
||||
// (a<<1) ⊕ 0¹²⁰10000111 if firstbit(a)=1
|
||||
//
|
||||
func (b *Block) Dbl() {
|
||||
var z byte
|
||||
|
||||
for i := Size - 1; i >= 0; i-- {
|
||||
zz := b[i] >> 7
|
||||
b[i] = b[i]<<1 | z
|
||||
z = zz
|
||||
}
|
||||
|
||||
b[Size-1] ^= byte(subtle.ConstantTimeSelect(int(z), R, 0))
|
||||
}
|
||||
|
||||
// Encrypt a block with the given block cipher
|
||||
func (b *Block) Encrypt(c cipher.Block) {
|
||||
c.Encrypt(b[:], b[:])
|
||||
}
|
114
vendor/github.com/miscreant/miscreant-go/cmac/cmac.go
generated
vendored
Normal file
114
vendor/github.com/miscreant/miscreant-go/cmac/cmac.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// CMAC message authentication code, defined in
|
||||
// NIST Special Publication SP 800-38B.
|
||||
|
||||
package cmac
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"hash"
|
||||
|
||||
"github.com/miscreant/miscreant-go/block"
|
||||
)
|
||||
|
||||
type cmac struct {
|
||||
// c is the block cipher we're using (i.e. AES-128 or AES-256)
|
||||
c cipher.Block
|
||||
|
||||
// k1 and k2 are CMAC subkeys (for finishing the tag)
|
||||
k1, k2 block.Block
|
||||
|
||||
// digest contains the PMAC tag-in-progress
|
||||
digest block.Block
|
||||
|
||||
// buffer contains a part of the input message, processed a block-at-a-time
|
||||
buf block.Block
|
||||
|
||||
// pos marks the end of plaintext in the buffer
|
||||
pos uint
|
||||
}
|
||||
|
||||
// New returns a new instance of a CMAC message authentication code
|
||||
// digest using the given cipher.Block.
|
||||
func New(c cipher.Block) hash.Hash {
|
||||
if c.BlockSize() != block.Size {
|
||||
panic("pmac: invalid cipher block size")
|
||||
}
|
||||
|
||||
d := new(cmac)
|
||||
d.c = c
|
||||
|
||||
// Subkey generation, p. 7
|
||||
d.k1.Encrypt(c)
|
||||
d.k1.Dbl()
|
||||
|
||||
copy(d.k2[:], d.k1[:])
|
||||
d.k2.Dbl()
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Reset clears the digest state, starting a new digest.
|
||||
func (d *cmac) Reset() {
|
||||
d.digest.Clear()
|
||||
d.buf.Clear()
|
||||
d.pos = 0
|
||||
}
|
||||
|
||||
// Write adds the given data to the digest state.
|
||||
func (d *cmac) Write(p []byte) (nn int, err error) {
|
||||
nn = len(p)
|
||||
left := block.Size - d.pos
|
||||
|
||||
if uint(len(p)) > left {
|
||||
xor(d.buf[d.pos:], p[:left])
|
||||
p = p[left:]
|
||||
d.buf.Encrypt(d.c)
|
||||
d.pos = 0
|
||||
}
|
||||
|
||||
for uint(len(p)) > block.Size {
|
||||
xor(d.buf[:], p[:block.Size])
|
||||
p = p[block.Size:]
|
||||
d.buf.Encrypt(d.c)
|
||||
}
|
||||
|
||||
if len(p) > 0 {
|
||||
xor(d.buf[d.pos:], p)
|
||||
d.pos += uint(len(p))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sum returns the CMAC digest, one cipher block in length,
|
||||
// of the data written with Write.
|
||||
func (d *cmac) Sum(in []byte) []byte {
|
||||
// Finish last block, mix in key, encrypt.
|
||||
// Don't edit ci, in case caller wants
|
||||
// to keep digesting after call to Sum.
|
||||
k := d.k1
|
||||
if d.pos < uint(len(d.digest)) {
|
||||
k = d.k2
|
||||
}
|
||||
for i := 0; i < len(d.buf); i++ {
|
||||
d.digest[i] = d.buf[i] ^ k[i]
|
||||
}
|
||||
if d.pos < uint(len(d.digest)) {
|
||||
d.digest[d.pos] ^= 0x80
|
||||
}
|
||||
d.digest.Encrypt(d.c)
|
||||
return append(in, d.digest[:]...)
|
||||
}
|
||||
|
||||
func (d *cmac) Size() int { return len(d.digest) }
|
||||
|
||||
func (d *cmac) BlockSize() int { return d.c.BlockSize() }
|
||||
|
||||
func xor(a, b []byte) {
|
||||
for i, v := range b {
|
||||
a[i] ^= v
|
||||
}
|
||||
}
|
201
vendor/github.com/miscreant/miscreant-go/pmac/pmac.go
generated
vendored
Normal file
201
vendor/github.com/miscreant/miscreant-go/pmac/pmac.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
// PMAC message authentication code, defined in
|
||||
// http://web.cs.ucdavis.edu/~rogaway/ocb/pmac.pdf
|
||||
|
||||
package pmac
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/subtle"
|
||||
"hash"
|
||||
"math/bits"
|
||||
|
||||
"github.com/miscreant/miscreant-go/block"
|
||||
)
|
||||
|
||||
// Number of L blocks to precompute (i.e. µ in the PMAC paper)
|
||||
// TODO: dynamically compute these as needed
|
||||
const precomputedBlocks = 31
|
||||
|
||||
type pmac struct {
|
||||
// c is the block cipher we're using (i.e. AES-128 or AES-256)
|
||||
c cipher.Block
|
||||
|
||||
// l is defined as follows (quoted from the PMAC paper):
|
||||
//
|
||||
// Equation 1:
|
||||
//
|
||||
// a · x =
|
||||
// a<<1 if firstbit(a)=0
|
||||
// (a<<1) ⊕ 0¹²⁰10000111 if firstbit(a)=1
|
||||
//
|
||||
// Equation 2:
|
||||
//
|
||||
// a · x⁻¹ =
|
||||
// a>>1 if lastbit(a)=0
|
||||
// (a>>1) ⊕ 10¹²⁰1000011 if lastbit(a)=1
|
||||
//
|
||||
// Let L(0) ← L. For i ∈ [1..µ], compute L(i) ← L(i − 1) · x by
|
||||
// Equation (1) using a shift and a conditional xor.
|
||||
//
|
||||
// Compute L(−1) ← L · x⁻¹ by Equation (2), using a shift and a
|
||||
// conditional xor.
|
||||
//
|
||||
// Save the values L(−1), L(0), L(1), L(2), ..., L(µ) in a table.
|
||||
// (Alternatively, [ed: as we have done in this codebase] defer computing
|
||||
// some or all of these L(i) values until the value is actually needed.)
|
||||
l [precomputedBlocks]block.Block
|
||||
|
||||
// lInv contains the multiplicative inverse (i.e. right shift) of the first
|
||||
// l-value, computed as described above, and is XORed into the tag in the
|
||||
// event the message length is a multiple of the block size
|
||||
lInv block.Block
|
||||
|
||||
// digest contains the PMAC tag-in-progress
|
||||
digest block.Block
|
||||
|
||||
// offset is a block specific tweak to the input message
|
||||
offset block.Block
|
||||
|
||||
// buf contains a part of the input message, processed a block-at-a-time
|
||||
buf block.Block
|
||||
|
||||
// pos marks the end of plaintext in the buf
|
||||
pos uint
|
||||
|
||||
// ctr is the number of blocks we have MAC'd so far
|
||||
ctr uint
|
||||
|
||||
// finished is set true when we are done processing a message, and forbids
|
||||
// any subsequent writes until we reset the internal state
|
||||
finished bool
|
||||
}
|
||||
|
||||
// New creates a new PMAC instance using the given cipher
|
||||
func New(c cipher.Block) hash.Hash {
|
||||
if c.BlockSize() != block.Size {
|
||||
panic("pmac: invalid cipher block size")
|
||||
}
|
||||
|
||||
d := new(pmac)
|
||||
d.c = c
|
||||
|
||||
var tmp block.Block
|
||||
tmp.Encrypt(c)
|
||||
|
||||
for i := range d.l {
|
||||
copy(d.l[i][:], tmp[:])
|
||||
tmp.Dbl()
|
||||
}
|
||||
|
||||
// Compute L(−1) ← L · x⁻¹:
|
||||
//
|
||||
// a>>1 if lastbit(a)=0
|
||||
// (a>>1) ⊕ 10¹²⁰1000011 if lastbit(a)=1
|
||||
//
|
||||
copy(tmp[:], d.l[0][:])
|
||||
lastBit := int(tmp[block.Size-1] & 0x01)
|
||||
|
||||
for i := block.Size - 1; i > 0; i-- {
|
||||
carry := byte(subtle.ConstantTimeSelect(int(tmp[i-1]&1), 0x80, 0))
|
||||
tmp[i] = (tmp[i] >> 1) | carry
|
||||
}
|
||||
|
||||
tmp[0] >>= 1
|
||||
tmp[0] ^= byte(subtle.ConstantTimeSelect(lastBit, 0x80, 0))
|
||||
tmp[block.Size-1] ^= byte(subtle.ConstantTimeSelect(lastBit, block.R>>1, 0))
|
||||
copy(d.lInv[:], tmp[:])
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
// Reset clears the digest state, starting a new digest.
|
||||
func (d *pmac) Reset() {
|
||||
d.digest.Clear()
|
||||
d.offset.Clear()
|
||||
d.buf.Clear()
|
||||
d.pos = 0
|
||||
d.ctr = 0
|
||||
d.finished = false
|
||||
}
|
||||
|
||||
// Write adds the given data to the digest state.
|
||||
func (d *pmac) Write(msg []byte) (int, error) {
|
||||
if d.finished {
|
||||
panic("pmac: already finished")
|
||||
}
|
||||
|
||||
var msgPos, msgLen, remaining uint
|
||||
msgLen = uint(len(msg))
|
||||
remaining = block.Size - d.pos
|
||||
|
||||
// Finish filling the internal buf with the message
|
||||
if msgLen > remaining {
|
||||
copy(d.buf[d.pos:], msg[:remaining])
|
||||
|
||||
msgPos += remaining
|
||||
msgLen -= remaining
|
||||
|
||||
d.processBuffer()
|
||||
}
|
||||
|
||||
// So long as we have more than a blocks worth of data, compute
|
||||
// whole-sized blocks at a time.
|
||||
for msgLen > block.Size {
|
||||
copy(d.buf[:], msg[msgPos:msgPos+block.Size])
|
||||
|
||||
msgPos += block.Size
|
||||
msgLen -= block.Size
|
||||
|
||||
d.processBuffer()
|
||||
}
|
||||
|
||||
if msgLen > 0 {
|
||||
copy(d.buf[d.pos:d.pos+msgLen], msg[msgPos:])
|
||||
d.pos += msgLen
|
||||
}
|
||||
|
||||
return len(msg), nil
|
||||
}
|
||||
|
||||
// Sum returns the PMAC digest, one cipher block in length,
|
||||
// of the data written with Write.
|
||||
func (d *pmac) Sum(in []byte) []byte {
|
||||
if d.finished {
|
||||
panic("pmac: already finished")
|
||||
}
|
||||
|
||||
if d.pos == block.Size {
|
||||
xor(d.digest[:], d.buf[:])
|
||||
xor(d.digest[:], d.lInv[:])
|
||||
} else {
|
||||
xor(d.digest[:], d.buf[:d.pos])
|
||||
d.digest[d.pos] ^= 0x80
|
||||
}
|
||||
|
||||
d.digest.Encrypt(d.c)
|
||||
d.finished = true
|
||||
|
||||
return append(in, d.digest[:]...)
|
||||
}
|
||||
|
||||
func (d *pmac) Size() int { return block.Size }
|
||||
|
||||
func (d *pmac) BlockSize() int { return block.Size }
|
||||
|
||||
// Update the internal tag state based on the buf contents
|
||||
func (d *pmac) processBuffer() {
|
||||
xor(d.offset[:], d.l[bits.TrailingZeros(d.ctr+1)][:])
|
||||
xor(d.buf[:], d.offset[:])
|
||||
d.ctr++
|
||||
|
||||
d.buf.Encrypt(d.c)
|
||||
xor(d.digest[:], d.buf[:])
|
||||
d.pos = 0
|
||||
}
|
||||
|
||||
// XOR the contents of b into a in-place
|
||||
func xor(a, b []byte) {
|
||||
for i, v := range b {
|
||||
a[i] ^= v
|
||||
}
|
||||
}
|
243
vendor/github.com/miscreant/miscreant-go/siv.go
generated
vendored
Normal file
243
vendor/github.com/miscreant/miscreant-go/siv.go
generated
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
// Originally written in 2015 by Dmitry Chestnykh.
|
||||
// Modified in 2017 by Tony Arcieri.
|
||||
//
|
||||
// Miscreant implements Synthetic Initialization Vector (SIV)-based
|
||||
// authenticated encryption using the AES block cipher (RFC 5297).
|
||||
|
||||
package miscreant
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"github.com/miscreant/miscreant-go/block"
|
||||
"github.com/miscreant/miscreant-go/cmac"
|
||||
"github.com/miscreant/miscreant-go/pmac"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// MaxAssociatedDataItems is the maximum number of associated data items
|
||||
const MaxAssociatedDataItems = 126
|
||||
|
||||
var (
|
||||
// ErrKeySize indicates the given key size is not supported
|
||||
ErrKeySize = errors.New("siv: bad key size")
|
||||
|
||||
// ErrNotAuthentic indicates a ciphertext is malformed or corrupt
|
||||
ErrNotAuthentic = errors.New("siv: authentication failed")
|
||||
|
||||
// ErrTooManyAssociatedDataItems indicates more than MaxAssociatedDataItems were given
|
||||
ErrTooManyAssociatedDataItems = errors.New("siv: too many associated data items")
|
||||
)
|
||||
|
||||
// Cipher is an instance of AES-SIV, configured with either AES-CMAC or
|
||||
// AES-PMAC as a message authentication code.
|
||||
type Cipher struct {
|
||||
// MAC function used to derive a synthetic IV and authenticate the message
|
||||
h hash.Hash
|
||||
|
||||
// Block cipher function used to encrypt the message
|
||||
b cipher.Block
|
||||
|
||||
// Internal buffers
|
||||
tmp1, tmp2 block.Block
|
||||
}
|
||||
|
||||
// NewAESCMACSIV returns a new AES-SIV cipher with the given key, which must be
|
||||
// twice as long as an AES key, either 32 or 64 bytes to select AES-128
|
||||
// (AES-CMAC-SIV-256), or AES-256 (AES-CMAC-SIV-512).
|
||||
func NewAESCMACSIV(key []byte) (c *Cipher, err error) {
|
||||
n := len(key)
|
||||
if n != 32 && n != 64 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
|
||||
macBlock, err := aes.NewCipher(key[:n/2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctrBlock, err := aes.NewCipher(key[n/2:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = new(Cipher)
|
||||
c.h = cmac.New(macBlock)
|
||||
c.b = ctrBlock
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// NewAESPMACSIV returns a new AES-SIV cipher with the given key, which must be
|
||||
// twice as long as an AES key, either 32 or 64 bytes to select AES-128
|
||||
// (AES-PMAC-SIV-256), or AES-256 (AES-PMAC-SIV-512).
|
||||
func NewAESPMACSIV(key []byte) (c *Cipher, err error) {
|
||||
n := len(key)
|
||||
if n != 32 && n != 64 {
|
||||
return nil, ErrKeySize
|
||||
}
|
||||
|
||||
macBlock, err := aes.NewCipher(key[:n/2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctrBlock, err := aes.NewCipher(key[n/2:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = new(Cipher)
|
||||
c.h = pmac.New(macBlock)
|
||||
c.b = ctrBlock
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Overhead returns the difference between plaintext and ciphertext lengths.
|
||||
func (c *Cipher) Overhead() int {
|
||||
return c.h.Size()
|
||||
}
|
||||
|
||||
// Seal encrypts and authenticates plaintext, authenticates the given
|
||||
// associated data items, and appends the result to dst, returning the updated
|
||||
// slice.
|
||||
//
|
||||
// The plaintext and dst may alias exactly or not at all.
|
||||
//
|
||||
// For nonce-based encryption, the nonce should be the last associated data item.
|
||||
func (c *Cipher) Seal(dst []byte, plaintext []byte, data ...[]byte) ([]byte, error) {
|
||||
if len(data) > MaxAssociatedDataItems {
|
||||
return nil, ErrTooManyAssociatedDataItems
|
||||
}
|
||||
|
||||
// Authenticate
|
||||
iv := c.s2v(data, plaintext)
|
||||
ret, out := sliceForAppend(dst, len(iv)+len(plaintext))
|
||||
copy(out, iv)
|
||||
|
||||
// Encrypt
|
||||
zeroIVBits(iv)
|
||||
ctr := cipher.NewCTR(c.b, iv)
|
||||
ctr.XORKeyStream(out[len(iv):], plaintext)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Open decrypts ciphertext, authenticates the decrypted plaintext and the given
|
||||
// associated data items and, if successful, appends the resulting plaintext
|
||||
// to dst, returning the updated slice. The additional data items must match the
|
||||
// items passed to Seal.
|
||||
//
|
||||
// The ciphertext and dst may alias exactly or not at all.
|
||||
//
|
||||
// For nonce-based encryption, the nonce should be the last associated data item.
|
||||
func (c *Cipher) Open(dst []byte, ciphertext []byte, data ...[]byte) ([]byte, error) {
|
||||
if len(data) > MaxAssociatedDataItems {
|
||||
return nil, ErrTooManyAssociatedDataItems
|
||||
}
|
||||
if len(ciphertext) < c.Overhead() {
|
||||
return nil, ErrNotAuthentic
|
||||
}
|
||||
|
||||
// Decrypt
|
||||
iv := c.tmp1[:c.Overhead()]
|
||||
copy(iv, ciphertext)
|
||||
zeroIVBits(iv)
|
||||
ctr := cipher.NewCTR(c.b, iv)
|
||||
ret, out := sliceForAppend(dst, len(ciphertext)-len(iv))
|
||||
ctr.XORKeyStream(out, ciphertext[len(iv):])
|
||||
|
||||
// Authenticate
|
||||
expected := c.s2v(data, out)
|
||||
if subtle.ConstantTimeCompare(ciphertext[:len(iv)], expected) != 1 {
|
||||
return nil, ErrNotAuthentic
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (c *Cipher) s2v(s [][]byte, sn []byte) []byte {
|
||||
h := c.h
|
||||
h.Reset()
|
||||
|
||||
tmp, d := c.tmp1, c.tmp2
|
||||
tmp.Clear()
|
||||
|
||||
// NOTE(dchest): The standalone S2V returns CMAC(1) if the number of
|
||||
// passed vectors is zero, however in SIV construction this case is
|
||||
// never triggered, since we always pass plaintext as the last vector
|
||||
// (even if it's zero-length), so we omit this case.
|
||||
|
||||
_, err := h.Write(tmp[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
copy(d[:], h.Sum(d[:0]))
|
||||
h.Reset()
|
||||
|
||||
for _, v := range s {
|
||||
_, err := h.Write(v)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
copy(tmp[:], h.Sum(tmp[:0]))
|
||||
h.Reset()
|
||||
d.Dbl()
|
||||
xor(d[:], tmp[:])
|
||||
}
|
||||
|
||||
tmp.Clear()
|
||||
|
||||
if len(sn) >= h.BlockSize() {
|
||||
n := len(sn) - len(d)
|
||||
copy(tmp[:], sn[n:])
|
||||
_, err = h.Write(sn[:n])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
copy(tmp[:], sn)
|
||||
tmp[len(sn)] = 0x80
|
||||
d.Dbl()
|
||||
}
|
||||
xor(tmp[:], d[:])
|
||||
_, err = h.Write(tmp[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return h.Sum(tmp[:0])
|
||||
}
|
||||
|
||||
func xor(a, b []byte) {
|
||||
for i, v := range b {
|
||||
a[i] ^= v
|
||||
}
|
||||
}
|
||||
|
||||
func zeroIVBits(iv []byte) {
|
||||
// "We zero-out the top bit in each of the last two 32-bit words
|
||||
// of the IV before assigning it to Ctr"
|
||||
// — http://web.cs.ucdavis.edu/~rogaway/papers/siv.pdf
|
||||
iv[len(iv)-8] &= 0x7f
|
||||
iv[len(iv)-4] &= 0x7f
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
173
vendor/github.com/miscreant/miscreant-go/stream.go
generated
vendored
Normal file
173
vendor/github.com/miscreant/miscreant-go/stream.go
generated
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
package miscreant
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// streamNoncePrefixSize is the user-supplied nonce size
|
||||
const streamNoncePrefixSize = 8
|
||||
|
||||
// streamExtendedNonceSize is the nonce prefix + 32-bit counter + 1-byte last block flag
|
||||
const streamExtendedNonceSize = streamNoncePrefixSize + 4 + 1
|
||||
|
||||
// lastBlockFlag indicates that a block is the last in the STREAM
|
||||
const lastBlockFlag byte = 1
|
||||
|
||||
// counterMax is the maximum allowable value for the stream counter
|
||||
const counterMax uint64 = 0xFFFFFFFF
|
||||
|
||||
// StreamEncryptor encrypts message streams, selecting the nonces using a
|
||||
// 32-bit counter, generalized for any cipher.AEAD algorithm
|
||||
//
|
||||
// This construction corresponds to the ℰ stream encryptor object as defined in
|
||||
// the paper Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance
|
||||
type StreamEncryptor struct {
|
||||
// cipher.AEAD instance underlying this STREAM
|
||||
a cipher.AEAD
|
||||
|
||||
// Nonce encoder instance which computes per-message nonces
|
||||
n *nonceEncoder32
|
||||
}
|
||||
|
||||
// NewStreamEncryptor returns a STREAM encryptor instance with the given
|
||||
// cipher, nonce, and a key which must be twice as long as an AES key, either
|
||||
// 32 or 64 bytes to select AES-128 (AES-SIV-256) or AES-256 (AES-SIV-512).
|
||||
func NewStreamEncryptor(alg string, key, nonce []byte) (*StreamEncryptor, error) {
|
||||
aead, err := NewAEAD(alg, key, streamExtendedNonceSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonceEncoder, err := newNonceEncoder32(nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StreamEncryptor{a: aead, n: nonceEncoder}, nil
|
||||
}
|
||||
|
||||
// NonceSize returns the size of the nonce that must be passed to
|
||||
// NewStreamEncryptor
|
||||
func (e *StreamEncryptor) NonceSize() int { return streamNoncePrefixSize }
|
||||
|
||||
// Overhead returns the maximum difference between the lengths of a
|
||||
// plaintext and its ciphertext, which in the case of AES-SIV modes
|
||||
// is the size of the initialization vector
|
||||
func (e *StreamEncryptor) Overhead() int { return e.a.Overhead() }
|
||||
|
||||
// Seal the next message in the STREAM, which encrypts and authenticates
|
||||
// plaintext, authenticates the additional data and appends the result to dst,
|
||||
// returning the updated slice.
|
||||
//
|
||||
// The plaintext and dst may alias exactly or not at all. To reuse
|
||||
// plaintext's storage for the encrypted output, use plaintext[:0] as dst.
|
||||
//
|
||||
// The lastBlock argument should be set to true if this is the last message
|
||||
// in the STREAM. No further messages can be encrypted after the last one
|
||||
func (e *StreamEncryptor) Seal(dst, plaintext, aData []byte, lastBlock bool) []byte {
|
||||
return e.a.Seal(dst, e.n.Next(lastBlock), plaintext, aData)
|
||||
}
|
||||
|
||||
// StreamDecryptor decrypts message streams, selecting the nonces using a
|
||||
// 32-bit counter, generalized for any cipher.AEAD algorithm
|
||||
//
|
||||
// This construction corresponds to the ℰ stream encryptor object as defined in
|
||||
// the paper Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance
|
||||
type StreamDecryptor struct {
|
||||
// cipher.AEAD instance underlying this STREAM
|
||||
a cipher.AEAD
|
||||
|
||||
// Nonce encoder instance which computes per-message nonces
|
||||
n *nonceEncoder32
|
||||
}
|
||||
|
||||
// NewStreamDecryptor returns a STREAM encryptor instance with the given
|
||||
// cipher, nonce, and a key which must be twice as long as an AES key, either
|
||||
// 32 or 64 bytes to select AES-128 (AES-SIV-256) or AES-256 (AES-SIV-512).
|
||||
func NewStreamDecryptor(alg string, key, nonce []byte) (*StreamDecryptor, error) {
|
||||
aead, err := NewAEAD(alg, key, streamExtendedNonceSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
nonceEncoder, err := newNonceEncoder32(nonce)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &StreamDecryptor{a: aead, n: nonceEncoder}, nil
|
||||
}
|
||||
|
||||
// NonceSize returns the size of the nonce that must be passed to
|
||||
// NewStreamDecryptor
|
||||
func (d *StreamDecryptor) NonceSize() int { return streamNoncePrefixSize }
|
||||
|
||||
// Overhead returns the maximum difference between the lengths of a
|
||||
// plaintext and its ciphertext, which in the case of AES-SIV modes
|
||||
// is the size of the initialization vector
|
||||
func (d *StreamDecryptor) Overhead() int { return d.a.Overhead() }
|
||||
|
||||
// Open decrypts and authenticates the next ciphertext in the STREAM,
|
||||
// and also authenticates the additional data, ensuring it matches
|
||||
// the value passed to Seal.
|
||||
//
|
||||
// If successful, it appends the resulting plaintext to dst and returns
|
||||
// the updated slice.
|
||||
//
|
||||
// The ciphertext and dst may alias exactly or not at all. To reuse
|
||||
// ciphertext's storage for the decrypted output, use ciphertext[:0] as dst.
|
||||
//
|
||||
// Even if the function fails, the contents of dst, up to its capacity,
|
||||
// may be overwritten.
|
||||
func (d *StreamDecryptor) Open(dst, ciphertext, aData []byte, lastBlock bool) ([]byte, error) {
|
||||
return d.a.Open(dst, d.n.Next(lastBlock), ciphertext, aData)
|
||||
}
|
||||
|
||||
// Computes STREAM nonces based on the current position in the STREAM.
|
||||
//
|
||||
// Accepts a 64-bit nonce and uses a 32-bit counter internally.
|
||||
//
|
||||
// Panics if the nonce size is incorrect, or the 32-bit counter overflows
|
||||
type nonceEncoder32 struct {
|
||||
value [streamExtendedNonceSize]byte
|
||||
counter uint64
|
||||
finished bool
|
||||
}
|
||||
|
||||
func newNonceEncoder32(noncePrefix []byte) (*nonceEncoder32, error) {
|
||||
if len(noncePrefix) != streamNoncePrefixSize {
|
||||
panic("miscreant.STREAM: incorrect nonce length")
|
||||
}
|
||||
|
||||
value := [streamExtendedNonceSize]byte{0}
|
||||
copy(value[:streamNoncePrefixSize], noncePrefix)
|
||||
|
||||
return &nonceEncoder32{
|
||||
value: value,
|
||||
counter: 0,
|
||||
finished: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *nonceEncoder32) Next(lastBlock bool) []byte {
|
||||
if n.finished {
|
||||
panic("miscreant.STREAM: already finished")
|
||||
}
|
||||
|
||||
counterSlice := n.value[streamNoncePrefixSize : streamNoncePrefixSize+4]
|
||||
binary.BigEndian.PutUint32(counterSlice, uint32(n.counter))
|
||||
|
||||
if lastBlock {
|
||||
n.value[len(n.value)-1] = lastBlockFlag
|
||||
n.finished = true
|
||||
} else {
|
||||
n.counter++
|
||||
if n.counter > counterMax {
|
||||
panic("miscreant.STREAM: nonce counter overflowed")
|
||||
}
|
||||
}
|
||||
|
||||
return n.value[:]
|
||||
}
|
Reference in New Issue
Block a user