Skip to content

Commit

Permalink
refactor(x448): Apply review feedback and improve readability
Browse files Browse the repository at this point in the history
  • Loading branch information
lubux committed Nov 22, 2023
1 parent 3198ed3 commit 0f7936b
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 58 deletions.
4 changes: 2 additions & 2 deletions openpgp/packet/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions openpgp/packet/public_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
114 changes: 63 additions & 51 deletions openpgp/x448/x448.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -72,70 +78,75 @@ 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)
// Compute shared key
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
}
Expand All @@ -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
}
4 changes: 2 additions & 2 deletions openpgp/x448/x448_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
Expand Down

0 comments on commit 0f7936b

Please sign in to comment.