Skip to content

Commit

Permalink
Merge pull request golang#18 from keybase/jacob/brainpool
Browse files Browse the repository at this point in the history
Add Brainpool Curves and Expand ECDSA support in openpgp
  • Loading branch information
Jacob H. Haven committed Mar 15, 2016
2 parents c9f75da + 06b6e81 commit b3390d1
Show file tree
Hide file tree
Showing 13 changed files with 560 additions and 6 deletions.
134 changes: 134 additions & 0 deletions brainpool/brainpool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Package brainpool implements Brainpool elliptic curves.
// Implementation of rcurves is from github.com/ebfe/brainpool
// Note that these curves are implemented with naive, non-constant time operations
// and are likely not suitable for enviroments where timing attacks are a concern.
package brainpool

import (
"crypto/elliptic"
"math/big"
"sync"
)

var (
once sync.Once
p256t1, p384t1, p512t1 *elliptic.CurveParams
p256r1, p384r1, p512r1 *rcurve
)

func initAll() {
initP256t1()
initP384t1()
initP512t1()
initP256r1()
initP384r1()
initP512r1()
}

func initP256t1() {
p256t1 = &elliptic.CurveParams{Name: "brainpoolP256t1"}
p256t1.P, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16)
p256t1.N, _ = new(big.Int).SetString("A9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7", 16)
p256t1.B, _ = new(big.Int).SetString("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16)
p256t1.Gx, _ = new(big.Int).SetString("A3E8EB3CC1CFE7B7732213B23A656149AFA142C47AAFBC2B79A191562E1305F4", 16)
p256t1.Gy, _ = new(big.Int).SetString("2D996C823439C56D7F7B22E14644417E69BCB6DE39D027001DABE8F35B25C9BE", 16)
p256t1.BitSize = 256
}

func initP256r1() {
twisted := p256t1
params := &elliptic.CurveParams{
Name: "brainpoolP256r1",
P: twisted.P,
N: twisted.N,
BitSize: twisted.BitSize,
}
params.Gx, _ = new(big.Int).SetString("8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262", 16)
params.Gy, _ = new(big.Int).SetString("547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997", 16)
z, _ := new(big.Int).SetString("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0", 16)
p256r1 = newrcurve(twisted, params, z)
}

func initP384t1() {
p384t1 = &elliptic.CurveParams{Name: "brainpoolP384t1"}
p384t1.P, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16)
p384t1.N, _ = new(big.Int).SetString("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565", 16)
p384t1.B, _ = new(big.Int).SetString("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16)
p384t1.Gx, _ = new(big.Int).SetString("18DE98B02DB9A306F2AFCD7235F72A819B80AB12EBD653172476FECD462AABFFC4FF191B946A5F54D8D0AA2F418808CC", 16)
p384t1.Gy, _ = new(big.Int).SetString("25AB056962D30651A114AFD2755AD336747F93475B7A1FCA3B88F2B6A208CCFE469408584DC2B2912675BF5B9E582928", 16)
p384t1.BitSize = 384
}

func initP384r1() {
twisted := p384t1
params := &elliptic.CurveParams{
Name: "brainpoolP384r1",
P: twisted.P,
N: twisted.N,
BitSize: twisted.BitSize,
}
params.Gx, _ = new(big.Int).SetString("1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E", 16)
params.Gy, _ = new(big.Int).SetString("8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315", 16)
z, _ := new(big.Int).SetString("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C", 16)
p384r1 = newrcurve(twisted, params, z)
}

func initP512t1() {
p512t1 = &elliptic.CurveParams{Name: "brainpoolP512t1"}
p512t1.P, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16)
p512t1.N, _ = new(big.Int).SetString("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069", 16)
p512t1.B, _ = new(big.Int).SetString("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16)
p512t1.Gx, _ = new(big.Int).SetString("640ECE5C12788717B9C1BA06CBC2A6FEBA85842458C56DDE9DB1758D39C0313D82BA51735CDB3EA499AA77A7D6943A64F7A3F25FE26F06B51BAA2696FA9035DA", 16)
p512t1.Gy, _ = new(big.Int).SetString("5B534BD595F5AF0FA2C892376C84ACE1BB4E3019B71634C01131159CAE03CEE9D9932184BEEF216BD71DF2DADF86A627306ECFF96DBB8BACE198B61E00F8B332", 16)
p512t1.BitSize = 512
}

func initP512r1() {
twisted := p512t1
params := &elliptic.CurveParams{
Name: "brainpoolP512r1",
P: twisted.P,
N: twisted.N,
BitSize: twisted.BitSize,
}
params.Gx, _ = new(big.Int).SetString("81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822", 16)
params.Gy, _ = new(big.Int).SetString("7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892", 16)
z, _ := new(big.Int).SetString("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB", 16)
p512r1 = newrcurve(twisted, params, z)
}

// P256t1 returns a Curve which implements Brainpool P256t1 (see RFC 5639, section 3.4)
func P256t1() elliptic.Curve {
once.Do(initAll)
return p256t1
}

// P256r1 returns a Curve which implements Brainpool P256r1 (see RFC 5639, section 3.4)
func P256r1() elliptic.Curve {
once.Do(initAll)
return p256r1
}

// P384t1 returns a Curve which implements Brainpool P384t1 (see RFC 5639, section 3.6)
func P384t1() elliptic.Curve {
once.Do(initAll)
return p384t1
}

// P384r1 returns a Curve which implements Brainpool P384r1 (see RFC 5639, section 3.6)
func P384r1() elliptic.Curve {
once.Do(initAll)
return p384r1
}

// P512t1 returns a Curve which implements Brainpool P512t1 (see RFC 5639, section 3.7)
func P512t1() elliptic.Curve {
once.Do(initAll)
return p512t1
}

// P512r1 returns a Curve which implements Brainpool P512r1 (see RFC 5639, section 3.7)
func P512r1() elliptic.Curve {
once.Do(initAll)
return p512r1
}
49 changes: 49 additions & 0 deletions brainpool/brainpool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package brainpool

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"testing"
)

func testCurve(t *testing.T, curve elliptic.Curve) {
priv, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
t.Fatal(err)
}

msg := []byte("test")
r, s, err := ecdsa.Sign(rand.Reader, priv, msg)
if err != nil {
t.Fatal(err)
}

if !ecdsa.Verify(&priv.PublicKey, msg, r, s) {
t.Fatal("signature didn't verify.")
}
}

func TestP256t1(t *testing.T) {
testCurve(t, P256t1())
}

func TestP256r1(t *testing.T) {
testCurve(t, P256r1())
}

func TestP384t1(t *testing.T) {
testCurve(t, P384t1())
}

func TestP384r1(t *testing.T) {
testCurve(t, P384r1())
}

func TestP512t1(t *testing.T) {
testCurve(t, P512t1())
}

func TestP512r1(t *testing.T) {
testCurve(t, P512r1())
}
83 changes: 83 additions & 0 deletions brainpool/rcurve.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package brainpool

import (
"crypto/elliptic"
"math/big"
)

var _ elliptic.Curve = (*rcurve)(nil)

type rcurve struct {
twisted elliptic.Curve
params *elliptic.CurveParams
z *big.Int
zinv *big.Int
z2 *big.Int
z3 *big.Int
zinv2 *big.Int
zinv3 *big.Int
}

var (
two = big.NewInt(2)
three = big.NewInt(3)
)

func newrcurve(twisted elliptic.Curve, params *elliptic.CurveParams, z *big.Int) *rcurve {
zinv := new(big.Int).ModInverse(z, params.P)
return &rcurve{
twisted: twisted,
params: params,
z: z,
zinv: zinv,
z2: new(big.Int).Exp(z, two, params.P),
z3: new(big.Int).Exp(z, three, params.P),
zinv2: new(big.Int).Exp(zinv, two, params.P),
zinv3: new(big.Int).Exp(zinv, three, params.P),
}
}

func (curve *rcurve) toTwisted(x, y *big.Int) (*big.Int, *big.Int) {
var tx, ty big.Int
tx.Mul(x, curve.z2)
tx.Mod(&tx, curve.params.P)
ty.Mul(y, curve.z3)
ty.Mod(&ty, curve.params.P)
return &tx, &ty
}

func (curve *rcurve) fromTwisted(tx, ty *big.Int) (*big.Int, *big.Int) {
var x, y big.Int
x.Mul(tx, curve.zinv2)
x.Mod(&x, curve.params.P)
y.Mul(ty, curve.zinv3)
y.Mod(&y, curve.params.P)
return &x, &y
}

func (curve *rcurve) Params() *elliptic.CurveParams {
return curve.params
}

func (curve *rcurve) IsOnCurve(x, y *big.Int) bool {
return curve.twisted.IsOnCurve(curve.toTwisted(x, y))
}

func (curve *rcurve) Add(x1, y1, x2, y2 *big.Int) (x, y *big.Int) {
tx1, ty1 := curve.toTwisted(x1, y1)
tx2, ty2 := curve.toTwisted(x2, y2)
return curve.fromTwisted(curve.twisted.Add(tx1, ty1, tx2, ty2))
}

func (curve *rcurve) Double(x1, y1 *big.Int) (x, y *big.Int) {
return curve.fromTwisted(curve.twisted.Double(curve.toTwisted(x1, y1)))
}

func (curve *rcurve) ScalarMult(x1, y1 *big.Int, scalar []byte) (x, y *big.Int) {
tx1, ty1 := curve.toTwisted(x1, y1)
return curve.fromTwisted(curve.twisted.ScalarMult(tx1, ty1, scalar))
}

func (curve *rcurve) ScalarBaseMult(scalar []byte) (x, y *big.Int) {
return curve.fromTwisted(curve.twisted.ScalarBaseMult(scalar))
}
117 changes: 117 additions & 0 deletions openpgp/brainpool_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package openpgp

import (
"bytes"
"os"
"strings"
"testing"

"github.com/keybase/go-crypto/openpgp/armor"
"github.com/keybase/go-crypto/openpgp/packet"
)

const msg = `Hello World!`

func signWithKeyFile(t *testing.T, name, password string) {
f, err := os.Open(name)
if err != nil {
t.Fatal(err)
}
defer f.Close()

es, err := ReadArmoredKeyRing(f)
if err != nil {
t.Fatal(err)
}

// Cycle through all entities we find a signing key.
for _, e := range es {
if err = e.PrivateKey.Decrypt([]byte(password)); err != nil {
continue
}

buf := new(bytes.Buffer)
if err = DetachSign(buf, e, strings.NewReader(msg), nil); err == nil {
p, err := packet.Read(buf)
if err != nil {
t.Fatal(err)
}
sig, ok := p.(*packet.Signature)
if !ok {
t.Fatal("couldn't parse signature from buffer")
}
signed := sig.Hash.New()
signed.Write([]byte(msg))
if err := e.PrimaryKey.VerifySignature(signed, sig); err != nil {
t.Fatal(err)
}

break
}
}
if err != nil {
t.Fatal(err)
}
}

func verifySig(t *testing.T, keyFile, sigFile string) {
var f *os.File
var err error
var b *armor.Block
var p packet.Packet

if f, err = os.Open(keyFile); err != nil {
t.Fatal(err)
}
defer f.Close()

if b, err = armor.Decode(f); err != nil {
t.Fatal(err)
}
if p, err = packet.Read(b.Body); err != nil {
t.Fatal(err)
}

priv, ok := p.(*packet.PrivateKey)
if !ok {
t.Fatal("couldn't parse private key")
}

if f, err = os.Open(sigFile); err != nil {
t.Fatal(err)
}
defer f.Close()

if b, err = armor.Decode(f); err != nil {
t.Fatal(err)
}
if p, err = packet.Read(b.Body); err != nil {
t.Fatal(err)
}

sig, ok := p.(*packet.Signature)
if !ok {
t.Fatal("couldn't parse signature")
}

signed := sig.Hash.New()
signed.Write([]byte(msg))
if err := priv.PublicKey.VerifySignature(signed, sig); err != nil {
t.Fatal(err)
}
}

func TestParseP256r1(t *testing.T) {
signWithKeyFile(t, "testdata/brainpoolP256r1.pgp", "256")
verifySig(t, "testdata/brainpoolP256r1.pgp", "testdata/brainpoolP256r1.asc")
}

func TestParseP384r1(t *testing.T) {
signWithKeyFile(t, "testdata/brainpoolP384r1.pgp", "384")
verifySig(t, "testdata/brainpoolP384r1.pgp", "testdata/brainpoolP384r1.asc")
}

func TestParseP512r1(t *testing.T) {
signWithKeyFile(t, "testdata/brainpoolP512r1.pgp", "512")
verifySig(t, "testdata/brainpoolP512r1.pgp", "testdata/brainpoolP512r1.asc")
}
Loading

0 comments on commit b3390d1

Please sign in to comment.