diff --git a/openpgp/packet/private_key.go b/openpgp/packet/private_key.go index dc7eb50a4..3f67b8c0a 100644 --- a/openpgp/packet/private_key.go +++ b/openpgp/packet/private_key.go @@ -986,9 +986,9 @@ func (pk *PrivateKey) parseX448PrivateKey(data []byte) (err error) { privateKey := x448.NewPrivateKey(*publicKey) privateKey.PublicKey = *publicKey - privateKey.Secret = make([]byte, x448.PointSize) + privateKey.Secret = make([]byte, x448.KeySize) - if len(data) != x448.PointSize { + if len(data) != x448.KeySize { err = errors.StructuralError("wrong x448 key size") } copy(privateKey.Secret, data) diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go index ae0a60e0c..a7c202d51 100644 --- a/openpgp/packet/public_key.go +++ b/openpgp/packet/public_key.go @@ -520,7 +520,7 @@ func (pk *PublicKey) parseX25519(r io.Reader) (err error) { } func (pk *PublicKey) parseX448(r io.Reader) (err error) { - point := make([]byte, x448.PointSize) + point := make([]byte, x448.KeySize) _, err = io.ReadFull(r, point) if err != nil { return @@ -630,7 +630,7 @@ func (pk *PublicKey) algorithmSpecificByteCount() int { case PubKeyAlgoX25519: length += x25519.KeySize case PubKeyAlgoX448: - length += x448.PointSize + length += x448.KeySize case PubKeyAlgoEd25519: length += ed25519.PublicKeySize case PubKeyAlgoEd448: @@ -985,7 +985,7 @@ func (pk *PublicKey) BitLength() (bitLength uint16, err error) { case PubKeyAlgoX25519: bitLength = x25519.KeySize * 8 case PubKeyAlgoX448: - bitLength = x448.PointSize * 8 + bitLength = x448.KeySize * 8 case PubKeyAlgoEd25519: bitLength = ed25519.PublicKeySize * 8 case PubKeyAlgoEd448: diff --git a/openpgp/x448/x448.go b/openpgp/x448/x448.go index c5212e5fa..5f223a759 100644 --- a/openpgp/x448/x448.go +++ b/openpgp/x448/x448.go @@ -11,19 +11,25 @@ import ( "golang.org/x/crypto/hkdf" ) -const hkdfInfo = "OpenPGP X448" -const aesKeySize = 32 -const PointSize = 56 +const ( + hkdfInfo = "OpenPGP X448" + aes256KeySize = 32 + // The size of a public or private key in bytes. + KeySize = x448lib.Size +) type PublicKey struct { + // Point represents the encoded elliptic curve point of the public key. Point []byte } type PrivateKey struct { PublicKey + // Secret represents the secret of the private key. Secret []byte } +// NewPrivateKey creates a new empty private key including the public key. func NewPrivateKey(key PublicKey) *PrivateKey { return &PrivateKey{ PublicKey: key, @@ -42,7 +48,7 @@ func Validate(pk *PrivateKey) (err error) { return nil } -// GenerateKey generates a new x448 key pair +// GenerateKey generates a new x448 key pair. func GenerateKey(rand io.Reader) (*PrivateKey, error) { var privateKey, publicKey x448lib.Key privateKeyOut := new(PrivateKey) @@ -72,46 +78,48 @@ func generateKey(rand io.Reader, privateKey *x448lib.Key, publicKey *x448lib.Key return nil } -// Encrypt encrpyts a sessionKey with x448 according to -// the OpenPGP crypto refresh specification section 5.1.6. The function assumes that the +// Encrypt encrypts a sessionKey with x448 according to +// the OpenPGP crypto refresh specification section 5.1.7. The function assumes that the // sessionKey has the correct format and padding according to the specification. func Encrypt(rand io.Reader, publicKey *PublicKey, sessionKey []byte) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, err error) { var ephemeralPrivate, ephemeralPublic, staticPublic, shared x448lib.Key - // Check that the input static public key has 32 bytes - if len(publicKey.Point) != PointSize { + // Check that the input static public key has 56 bytes. + if len(publicKey.Point) != KeySize { err = errors.KeyInvalidError("x448: the public key has the wrong size") - return + return nil, nil, err } copy(staticPublic[:], publicKey.Point) - // Generate ephemeral keyPair - err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic) - if err != nil { - return + // Generate ephemeral keyPair. + if err = generateKey(rand, &ephemeralPrivate, &ephemeralPublic); err != nil { + return nil, nil, err } - // Compute shared key + // Compute shared key. ok := x448lib.Shared(&shared, &ephemeralPrivate, &staticPublic) if !ok { err = errors.KeyInvalidError("x448: the public key is a low order point") - return + return nil, nil, err } - // Derive the encryption key from the shared secret + // Derive the encryption key from the shared secret. encryptionKey := applyHKDF(ephemeralPublic[:], publicKey.Point[:], shared[:]) ephemeralPublicKey = &PublicKey{ Point: ephemeralPublic[:], } - // Encrypt the sessionKey with aes key wrapping + // Encrypt the sessionKey with aes key wrapping. encryptedSessionKey, err = keywrap.Wrap(encryptionKey, sessionKey) - return + if err != nil { + return nil, nil, err + } + return ephemeralPublicKey, encryptedSessionKey, nil } // Decrypt decrypts a session key stored in ciphertext with the provided x448 -// private key and ephemeral public key +// private key and ephemeral public key. func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext []byte) (encodedSessionKey []byte, err error) { var ephemeralPublic, staticPrivate, shared x448lib.Key - // Check that the input ephemeral public key has 32 bytes - if len(ephemeralPublicKey.Point) != PointSize { + // Check that the input ephemeral public key has 56 bytes + if len(ephemeralPublicKey.Point) != KeySize { err = errors.KeyInvalidError("x448: the public key has the wrong size") - return + return nil, err } copy(ephemeralPublic[:], ephemeralPublicKey.Point) subtle.ConstantTimeCopy(1, staticPrivate[:], privateKey.Secret) @@ -119,23 +127,26 @@ func Decrypt(privateKey *PrivateKey, ephemeralPublicKey *PublicKey, ciphertext [ ok := x448lib.Shared(&shared, &staticPrivate, &ephemeralPublic) if !ok { err = errors.KeyInvalidError("x448: the ephemeral public key is a low order point") - return + return nil, err } // Derive the encryption key from the shared secret encryptionKey := applyHKDF(ephemeralPublicKey.Point[:], privateKey.PublicKey.Point[:], shared[:]) // Decrypt the session key with aes key wrapping encodedSessionKey, err = keywrap.Unwrap(encryptionKey, ciphertext) - return + if err != nil { + return nil, err + } + return encodedSessionKey, nil } func applyHKDF(ephemeralPublicKey []byte, publicKey []byte, sharedSecret []byte) []byte { - inputKey := make([]byte, 3*PointSize) + inputKey := make([]byte, 3*KeySize) // ephemeral public key | recipient public key | shared secret - subtle.ConstantTimeCopy(1, inputKey[:PointSize], ephemeralPublicKey) - subtle.ConstantTimeCopy(1, inputKey[PointSize:2*PointSize], publicKey) - subtle.ConstantTimeCopy(1, inputKey[2*PointSize:], sharedSecret) + subtle.ConstantTimeCopy(1, inputKey[:KeySize], ephemeralPublicKey) + subtle.ConstantTimeCopy(1, inputKey[KeySize:2*KeySize], publicKey) + subtle.ConstantTimeCopy(1, inputKey[2*KeySize:], sharedSecret) hkdfReader := hkdf.New(sha512.New, inputKey, []byte{}, []byte(hkdfInfo)) - encryptionKey := make([]byte, aesKeySize) + encryptionKey := make([]byte, aes256KeySize) _, _ = io.ReadFull(hkdfReader, encryptionKey) return encryptionKey } @@ -151,67 +162,68 @@ func constantTimeIsZero(bytes []byte) bool { // ENCODING/DECODING ciphertexts: // EncodeFieldsLength returns the length of the ciphertext encoding -// given the encrpyted session key. +// given the encrypted session key. func EncodedFieldsLength(encryptedSessionKey []byte, v6 bool) int { lenCipherFunction := 0 if !v6 { lenCipherFunction = 1 } - return PointSize + 1 + len(encryptedSessionKey) + lenCipherFunction + return KeySize + 1 + len(encryptedSessionKey) + lenCipherFunction } -// EncodeField encodes x448 session key encryption as +// EncodeField encodes x448 session key encryption fields as // ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey -// and writes it to writer +// and writes it to writer. func EncodeFields(writer io.Writer, ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, v6 bool) (err error) { lenAlgorithm := 0 if !v6 { lenAlgorithm = 1 } if _, err = writer.Write(ephemeralPublicKey.Point); err != nil { - return + return err } if _, err = writer.Write([]byte{byte(len(encryptedSessionKey) + lenAlgorithm)}); err != nil { - return + return err } if !v6 { if _, err = writer.Write([]byte{cipherFunction}); err != nil { - return + return err } } - _, err = writer.Write(encryptedSessionKey) - return + if _, err = writer.Write(encryptedSessionKey); err != nil { + return err + } + return nil } // DecodeField decodes a x448 session key encryption as -// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey +// ephemeral x448 public key | follow byte length | cipherFunction (v3 only) | encryptedSessionKey. func DecodeFields(reader io.Reader, v6 bool) (ephemeralPublicKey *PublicKey, encryptedSessionKey []byte, cipherFunction byte, err error) { var buf [1]byte ephemeralPublicKey = &PublicKey{ - Point: make([]byte, PointSize), + Point: make([]byte, KeySize), } // 56 octets representing an ephemeral X448 public key. - _, err = io.ReadFull(reader, ephemeralPublicKey.Point) - if err != nil { - return + if _, err = io.ReadFull(reader, ephemeralPublicKey.Point); err != nil { + return nil, nil, 0, err } // A one-octet size of the following fields. - _, err = io.ReadFull(reader, buf[:]) - if err != nil { - return + if _, err = io.ReadFull(reader, buf[:]); err != nil { + return nil, nil, 0, err } followingLen := buf[0] // The one-octet algorithm identifier, if it was passed (in the case of a v3 PKESK packet). if !v6 { - _, err = io.ReadFull(reader, buf[:]) - if err != nil { - return + if _, err = io.ReadFull(reader, buf[:]); err != nil { + return nil, nil, 0, err } cipherFunction = buf[0] followingLen -= 1 } // The encrypted session key. encryptedSessionKey = make([]byte, followingLen) - _, err = io.ReadFull(reader, encryptedSessionKey) - return + if _, err = io.ReadFull(reader, encryptedSessionKey); err != nil { + return nil, nil, 0, err + } + return ephemeralPublicKey, encryptedSessionKey, cipherFunction, nil } diff --git a/openpgp/x448/x448_test.go b/openpgp/x448/x448_test.go index 73d38b1da..8b3e74db0 100644 --- a/openpgp/x448/x448_test.go +++ b/openpgp/x448/x448_test.go @@ -11,10 +11,10 @@ func TestGenerate(t *testing.T) { if err != nil { t.Fatal(err) } - if len(privateKey.Secret) != PointSize { + if len(privateKey.Secret) != KeySize { t.Fatal("key has the wrong size") } - if len(privateKey.PublicKey.Point) != PointSize { + if len(privateKey.PublicKey.Point) != KeySize { t.Fatal("key has the wrong size") } }