Skip to content

Commit

Permalink
Handle (un)marshalling at the ecdsa/eddsa/ecdh level
Browse files Browse the repository at this point in the history
  • Loading branch information
wussler committed Aug 10, 2022
1 parent 06aaa01 commit f2de896
Show file tree
Hide file tree
Showing 13 changed files with 321 additions and 85 deletions.
59 changes: 52 additions & 7 deletions openpgp/ecdh/ecdh.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type KDF struct {
}

type PublicKey struct {
Curve ecc.ECDHCurve
curve ecc.ECDHCurve
X, Y *big.Int
KDF
}
Expand All @@ -33,9 +33,54 @@ type PrivateKey struct {
D []byte
}

func NewPublicKey(curve ecc.ECDHCurve, kdfHash algorithm.Hash, kdfCipher algorithm.Cipher) *PublicKey {
return &PublicKey{
curve: curve,
KDF: KDF{
Hash: kdfHash,
Cipher: kdfCipher,
},
}
}

func NewPrivateKey(key PublicKey) *PrivateKey {
return &PrivateKey{
PublicKey: key,
}
}

func (pk *PublicKey) GetCurve() ecc.ECDHCurve {
return pk.curve
}

func (pk *PublicKey) MarshalPoint() []byte {
return pk.curve.MarshalPoint(pk.X, pk.Y)
}

func (pk *PublicKey) UnmarshalPoint(p []byte) error {
pk.X, pk.Y = pk.curve.UnmarshalPoint(p)
if pk.X == nil {
return errors.New("ecdh: failed to parse EC point")
}
return nil
}

func (sk *PrivateKey) MarshalByteSecret() []byte {
return sk.curve.MarshalByteSecret(sk.D)
}

func (sk *PrivateKey) UnmarshalByteSecret(d []byte) error {
sk.D = sk.curve.UnmarshalByteSecret(d)

if sk.D == nil {
return errors.New("ecdh: failed to parse scalar")
}
return nil
}

func GenerateKey(rand io.Reader, c ecc.ECDHCurve, kdf KDF) (priv *PrivateKey, err error) {
priv = new(PrivateKey)
priv.PublicKey.Curve = c
priv.PublicKey.curve = c
priv.PublicKey.KDF = kdf
priv.PublicKey.X, priv.PublicKey.Y, priv.D, err = c.GenerateECDH(rand)
return
Expand All @@ -54,7 +99,7 @@ func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte
}
m := append(msg, padding...)

vsG, zb, err := pub.Curve.Encaps(random, pub.X, pub.Y)
vsG, zb, err := pub.curve.Encaps(random, pub.X, pub.Y)
if err != nil {
return nil, nil, err
}
Expand All @@ -74,9 +119,9 @@ func Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte

func Decrypt(priv *PrivateKey, vsG, c, curveOID, fingerprint []byte) (msg []byte, err error) {
var m []byte
zb, err := priv.PublicKey.Curve.Decaps(vsG, priv.D)
zb, err := priv.PublicKey.curve.Decaps(vsG, priv.D)

for i := 0; i < priv.PublicKey.Curve.GetBuildKeyAttempts(); i++ {
for i := 0; i < priv.PublicKey.curve.GetBuildKeyAttempts(); i++ {
// RFC6637 §8: "Compute Z = KDF( S, Z_len, Param );"
// Try buildKey three times for compat, see comments in buildKey.
z, err := buildKey(&priv.PublicKey, zb, curveOID, fingerprint, i == 1, i == 2)
Expand All @@ -91,7 +136,7 @@ func Decrypt(priv *PrivateKey, vsG, c, curveOID, fingerprint []byte) (msg []byte
}
}

// Only return an error after we've tried all variants of buildKey.
// Only return an error after we've tried all (required) variants of buildKey.
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -155,5 +200,5 @@ func buildKey(pub *PublicKey, zb []byte, curveOID, fingerprint []byte, stripLead
}

func Validate(priv *PrivateKey) error {
return priv.Curve.Validate(priv.X, priv.Y, priv.D)
return priv.curve.Validate(priv.X, priv.Y, priv.D)
}
25 changes: 24 additions & 1 deletion openpgp/ecdh/ecdh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ func TestCurves(t *testing.T) {
priv := testGenerate(t, ECDHCurve)
testEncryptDecrypt(t, priv, curve.Oid.Bytes(), testFingerprint)
testValidation(t, priv)

// Needs fresh key
priv = testGenerate(t, ECDHCurve)
testMarshalUnmarshal(t, priv)
})
}
}
Expand Down Expand Up @@ -80,4 +84,23 @@ func testValidation(t *testing.T, priv *PrivateKey) {
if err := Validate(priv); err == nil {
t.Fatalf("failed to detect invalid key")
}
}
}

func testMarshalUnmarshal(t *testing.T, priv *PrivateKey) {
p := priv.MarshalPoint()
d := priv.MarshalByteSecret()

parsed := NewPrivateKey(*NewPublicKey(priv.GetCurve(), priv.KDF.Hash, priv.KDF.Cipher))

if err := parsed.UnmarshalPoint(p); err != nil {
t.Fatalf("unable to unmarshal point: %s", err)
}

if err := parsed.UnmarshalByteSecret(d); err != nil {
t.Fatalf("unable to unmarshal integer: %s", err)
}

if priv.X.Cmp(parsed.X) != 0 || (priv.Y != nil && priv.Y.Cmp(parsed.Y) != 0) || !bytes.Equal(priv.D, parsed.D) {
t.Fatal("failed to marshal/unmarshal correctly")
}
}
52 changes: 47 additions & 5 deletions openpgp/ecdsa/ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,78 @@
package ecdsa

import (
"errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"io"
"math/big"
)

type PublicKey struct {
X, Y *big.Int
Curve ecc.ECDSACurve
curve ecc.ECDSACurve
}

type PrivateKey struct {
PublicKey
D *big.Int
}

func NewPublicKey(curve ecc.ECDSACurve) *PublicKey {
return &PublicKey{
curve: curve,
}
}

func NewPrivateKey(key PublicKey) *PrivateKey {
return &PrivateKey{
PublicKey: key,
}
}

func (pk *PublicKey) GetCurve() ecc.ECDSACurve {
return pk.curve
}

func (pk *PublicKey) MarshalPoint() []byte {
return pk.curve.MarshalPoint(pk.X, pk.Y)
}

func (pk *PublicKey) UnmarshalPoint(p []byte) error {
pk.X, pk.Y = pk.curve.UnmarshalPoint(p)
if pk.X == nil {
return errors.New("ecdsa: failed to parse EC point")
}
return nil
}

func (sk *PrivateKey) MarshalIntegerSecret() []byte {
return sk.curve.MarshalIntegerSecret(sk.D)
}

func (sk *PrivateKey) UnmarshalIntegerSecret(d []byte) error {
sk.D = sk.curve.UnmarshalIntegerSecret(d)

if sk.D == nil {
return errors.New("ecdsa: failed to parse scalar")
}
return nil
}

func GenerateKey(rand io.Reader, c ecc.ECDSACurve) (priv *PrivateKey, err error) {
priv = new(PrivateKey)
priv.PublicKey.Curve = c
priv.PublicKey.curve = c
priv.PublicKey.X, priv.PublicKey.Y, priv.D, err = c.GenerateECDSA(rand)
return
}

func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error) {
return priv.PublicKey.Curve.Sign(rand, priv.X, priv.Y, priv.D, hash)
return priv.PublicKey.curve.Sign(rand, priv.X, priv.Y, priv.D, hash)
}

func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool {
return pub.Curve.Verify(pub.X, pub.Y, hash, r, s)
return pub.curve.Verify(pub.X, pub.Y, hash, r, s)
}

func Validate(priv *PrivateKey) error {
return priv.Curve.Validate(priv.X, priv.Y, priv.D.Bytes())
return priv.curve.Validate(priv.X, priv.Y, priv.D.Bytes())
}
27 changes: 26 additions & 1 deletion openpgp/ecdsa/ecdsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ package ecdsa

import (
"crypto/rand"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"io"
"math/big"
"testing"

"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
)

func TestCurves(t *testing.T) {
Expand All @@ -24,9 +25,14 @@ func TestCurves(t *testing.T) {
t.Fatal(err)
}


priv := testGenerate(t, ECDSACurve)
testSignVerify(t, priv)
testValidation(t, priv)

// Needs fresh key
priv = testGenerate(t, ECDSACurve)
testMarshalUnmarshal(t, priv)
})
}
}
Expand Down Expand Up @@ -69,3 +75,22 @@ func testValidation(t *testing.T, priv *PrivateKey) {
t.Fatal("failed to detect invalid key")
}
}

func testMarshalUnmarshal(t *testing.T, priv *PrivateKey) {
p := priv.MarshalPoint()
d := priv.MarshalIntegerSecret()

parsed := NewPrivateKey(*NewPublicKey(priv.GetCurve()))

if err := parsed.UnmarshalPoint(p); err != nil {
t.Fatalf("unable to unmarshal point: %s", err)
}

if err := parsed.UnmarshalIntegerSecret(d); err != nil {
t.Fatalf("unable to unmarshal integer: %s", err)
}

if priv.X.Cmp(parsed.X) != 0 || priv.Y.Cmp(parsed.Y) != 0 || priv.D.Cmp(parsed.D) != 0{
t.Fatal("failed to marshal/unmarshal correctly")
}
}
53 changes: 48 additions & 5 deletions openpgp/eddsa/eddsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,78 @@
package eddsa

import (
"errors"
"github.com/ProtonMail/go-crypto/openpgp/internal/ecc"
"io"
)

type PublicKey struct {
X []byte
Curve ecc.EdDSACurve
curve ecc.EdDSACurve
}

type PrivateKey struct {
PublicKey
D []byte
}

func NewPublicKey(curve ecc.EdDSACurve) *PublicKey {
return &PublicKey{
curve: curve,
}
}

func NewPrivateKey(key PublicKey) *PrivateKey {
return &PrivateKey{
PublicKey: key,
}
}

func (pk *PublicKey) GetCurve() ecc.EdDSACurve {
return pk.curve
}

func (pk *PublicKey) MarshalPoint() []byte {
return pk.curve.MarshalPoint(pk.X)
}

func (pk *PublicKey) UnmarshalPoint(x []byte) error {
pk.X = pk.curve.UnmarshalPoint(x)

if pk.X == nil {
return errors.New("eddsa: failed to parse EC point")
}
return nil
}

func (sk *PrivateKey) MarshalByteSecret() []byte {
return sk.curve.MarshalByteSecret(sk.D)
}

func (sk *PrivateKey) UnmarshalByteSecret(d []byte) error {
sk.D = sk.curve.UnmarshalByteSecret(d)

if sk.D == nil {
return errors.New("eddsa: failed to parse scalar")
}
return nil
}

func GenerateKey(rand io.Reader, c ecc.EdDSACurve) (priv *PrivateKey, err error) {
priv = new(PrivateKey)
priv.PublicKey.Curve = c
priv.PublicKey.curve = c
priv.PublicKey.X, priv.D, err = c.GenerateEdDSA(rand)
return
}

func Sign(priv *PrivateKey, message []byte) (r, s []byte, err error) {
return priv.PublicKey.Curve.Sign(priv.PublicKey.X, priv.D, message)
return priv.PublicKey.curve.Sign(priv.PublicKey.X, priv.D, message)
}

func Verify(pub *PublicKey, message, r, s []byte) bool {
return pub.Curve.Verify(pub.X, message, r, s)
return pub.curve.Verify(pub.X, message, r, s)
}

func Validate(priv *PrivateKey) error {
return priv.Curve.Validate(priv.PublicKey.X, priv.D)
return priv.curve.Validate(priv.PublicKey.X, priv.D)
}
Loading

0 comments on commit f2de896

Please sign in to comment.