x/crypto/openpgp/internal/encoding: add Field, MPI & OID types
The Field interface represents an encoded packet field. MPI is a
Multiprecision Integer field as defined in rfc 4880, Section 3.2. OID is
a variable length field as defined in rfc 6637, Section 8.
By using the MPI and OID types for packet fields, the packet.ecdsaKey
and packet.ecdhKdf types are replaced with the parseECDSA and parseECDH
methods of packet.PublicKey.
Change-Id: I8a7c16f8ab77c0aad3ed37540e606d9285907a5d
diff --git a/openpgp/internal/encoding/encoding.go b/openpgp/internal/encoding/encoding.go
new file mode 100644
index 0000000..6c92148
--- /dev/null
+++ b/openpgp/internal/encoding/encoding.go
@@ -0,0 +1,27 @@
+// Copyright 2017 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.
+
+// Package encoding implements openpgp packet field encodings as specified in
+// RFC 4880 and 6637.
+package encoding
+
+import "io"
+
+// Field is an encoded field of an openpgp packet.
+type Field interface {
+ // Bytes returns the decoded data.
+ Bytes() []byte
+
+ // BitLength is the size in bits of the decoded data.
+ BitLength() uint16
+
+ // EncodedBytes returns the encoded data.
+ EncodedBytes() []byte
+
+ // EncodedLength is the size in bytes of the encoded data.
+ EncodedLength() uint16
+
+ // ReadFrom reads the next Field from r.
+ ReadFrom(r io.Reader) (int64, error)
+}
diff --git a/openpgp/internal/encoding/mpi.go b/openpgp/internal/encoding/mpi.go
new file mode 100644
index 0000000..636046a
--- /dev/null
+++ b/openpgp/internal/encoding/mpi.go
@@ -0,0 +1,85 @@
+// Copyright 2017 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.
+
+package encoding
+
+import (
+ "io"
+ "math/big"
+)
+
+// An MPI is used to store the contents of a big integer, along with the bit
+// length that was specified in the original input. This allows the MPI to be
+// reserialized exactly.
+type MPI struct {
+ bytes []byte
+ bitLength uint16
+}
+
+// NewMPI returns a MPI initialized with bytes.
+func NewMPI(bytes []byte) *MPI {
+ return &MPI{
+ bytes: bytes,
+ bitLength: 8 * uint16(len(bytes)),
+ }
+}
+
+// Bytes returns the decoded data.
+func (m *MPI) Bytes() []byte {
+ return m.bytes
+}
+
+// BitLength is the size in bits of the decoded data.
+func (m *MPI) BitLength() uint16 {
+ return m.bitLength
+}
+
+// EncodedBytes returns the encoded data.
+func (m *MPI) EncodedBytes() []byte {
+ return append([]byte{byte(m.bitLength >> 8), byte(m.bitLength)}, m.bytes...)
+}
+
+// EncodedLength is the size in bytes of the encoded data.
+func (m *MPI) EncodedLength() uint16 {
+ return uint16(2 + len(m.bytes))
+}
+
+// ReadFrom reads into m the next MPI from r.
+func (m *MPI) ReadFrom(r io.Reader) (int64, error) {
+ var buf [2]byte
+ n, err := io.ReadFull(r, buf[0:])
+ if err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return int64(n), err
+ }
+
+ m.bitLength = uint16(buf[0])<<8 | uint16(buf[1])
+ m.bytes = make([]byte, (int(m.bitLength)+7)/8)
+
+ nn, err := io.ReadFull(r, m.bytes)
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+
+ // remove leading zero bytes from malformed GnuPG encoded MPIs:
+ // https://bugs.gnupg.org/gnupg/issue1853
+ for _, b := range m.bytes {
+ if b != 0 {
+ break
+ }
+ m.bytes = m.bytes[1:]
+ m.bitLength -= 8
+ }
+
+ return int64(n) + int64(nn), err
+}
+
+// SetBig initializes m with the bits from n.
+func (m *MPI) SetBig(n *big.Int) *MPI {
+ m.bytes = n.Bytes()
+ m.bitLength = uint16(n.BitLen())
+ return m
+}
diff --git a/openpgp/internal/encoding/mpi_test.go b/openpgp/internal/encoding/mpi_test.go
new file mode 100644
index 0000000..f319529
--- /dev/null
+++ b/openpgp/internal/encoding/mpi_test.go
@@ -0,0 +1,78 @@
+// Copyright 2017 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.
+
+package encoding
+
+import (
+ "bytes"
+ "io"
+ "testing"
+)
+
+var mpiTests = []struct {
+ encoded []byte
+ bytes []byte
+ reencoded []byte
+ bitLength uint16
+ err error
+}{
+ {
+ encoded: []byte{0x0, 0x1, 0x1},
+ bytes: []byte{0x1},
+ bitLength: 1,
+ },
+ {
+ encoded: []byte{0x0, 0x9, 0x1, 0xff},
+ bytes: []byte{0x1, 0xff},
+ bitLength: 9,
+ },
+ {
+ encoded: append([]byte{0x1, 0x0, 0xff}, make([]byte, 0x1f)...),
+ bytes: append([]byte{0xff}, make([]byte, 0x1f)...),
+ bitLength: 0x100,
+ },
+ // https://bugs.gnupg.org/gnupg/issue1853
+ {
+ encoded: []byte{0x0, 0x10, 0x0, 0x01},
+ bytes: []byte{0x01},
+ reencoded: []byte{0x0, 0x8, 0x01},
+ bitLength: 8,
+ },
+ // EOF error,
+ {
+ encoded: []byte{},
+ err: io.ErrUnexpectedEOF,
+ },
+ {
+ encoded: []byte{0x1, 0x0, 0x0},
+ err: io.ErrUnexpectedEOF,
+ },
+}
+
+func TestMPI(t *testing.T) {
+ for i, test := range mpiTests {
+ mpi := new(MPI)
+ if _, err := mpi.ReadFrom(bytes.NewBuffer(test.encoded)); err != nil {
+ if !sameError(err, test.err) {
+ t.Errorf("#%d: ReadFrom error got:%q", i, err)
+ }
+ continue
+ }
+ if b := mpi.Bytes(); !bytes.Equal(b, test.bytes) {
+ t.Errorf("#%d: bad creation got:%x want:%x", i, b, test.bytes)
+ }
+ if bl := mpi.BitLength(); bl != test.bitLength {
+ t.Errorf("#%d: bad BitLength got:%d want:%d", i, bl, test.bitLength)
+ }
+
+ reencoded := test.encoded
+ if test.reencoded != nil {
+ reencoded = test.reencoded
+ }
+
+ if b := mpi.EncodedBytes(); !bytes.Equal(b, reencoded) {
+ t.Errorf("#%d: bad encoding got:%x want:%x", i, b, reencoded)
+ }
+ }
+}
diff --git a/openpgp/internal/encoding/oid.go b/openpgp/internal/encoding/oid.go
new file mode 100644
index 0000000..92eeab9
--- /dev/null
+++ b/openpgp/internal/encoding/oid.go
@@ -0,0 +1,88 @@
+// Copyright 2017 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.
+
+package encoding
+
+import (
+ "io"
+
+ "golang.org/x/crypto/openpgp/errors"
+)
+
+// OID is used to store a variable-length field with a one-octet size
+// prefix. See https://tools.ietf.org/html/rfc6637#section-9.
+type OID struct {
+ bytes []byte
+}
+
+const (
+ // maxOID is the maximum number of bytes in a OID.
+ maxOID = 254
+ // reservedOIDLength1 and reservedOIDLength2 are OID lengths that the RFC
+ // specifies are reserved.
+ reservedOIDLength1 = 0
+ reservedOIDLength2 = 0xff
+)
+
+// NewOID returns a OID initialized with bytes.
+func NewOID(bytes []byte) *OID {
+ switch len(bytes) {
+ case reservedOIDLength1, reservedOIDLength2:
+ panic("encoding: NewOID argument length is reserved")
+ default:
+ if len(bytes) > maxOID {
+ panic("encoding: NewOID argment too large")
+ }
+ }
+
+ return &OID{
+ bytes: bytes,
+ }
+}
+
+// Bytes returns the decoded data.
+func (o *OID) Bytes() []byte {
+ return o.bytes
+}
+
+// BitLength is the size in bits of the decoded data.
+func (o *OID) BitLength() uint16 {
+ return uint16(len(o.bytes) * 8)
+}
+
+// EncodedBytes returns the encoded data.
+func (o *OID) EncodedBytes() []byte {
+ return append([]byte{byte(len(o.bytes))}, o.bytes...)
+}
+
+// EncodedLength is the size in bytes of the encoded data.
+func (o *OID) EncodedLength() uint16 {
+ return uint16(1 + len(o.bytes))
+}
+
+// ReadFrom reads into b the next OID from r.
+func (o *OID) ReadFrom(r io.Reader) (int64, error) {
+ var buf [1]byte
+ n, err := io.ReadFull(r, buf[:])
+ if err != nil {
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+ return int64(n), err
+ }
+
+ switch buf[0] {
+ case reservedOIDLength1, reservedOIDLength2:
+ return int64(n), errors.UnsupportedError("reserved for future extensions")
+ }
+
+ o.bytes = make([]byte, buf[0])
+
+ nn, err := io.ReadFull(r, o.bytes)
+ if err == io.EOF {
+ err = io.ErrUnexpectedEOF
+ }
+
+ return int64(n) + int64(nn), err
+}
diff --git a/openpgp/internal/encoding/oid_test.go b/openpgp/internal/encoding/oid_test.go
new file mode 100644
index 0000000..986f8fb
--- /dev/null
+++ b/openpgp/internal/encoding/oid_test.go
@@ -0,0 +1,86 @@
+// Copyright 2017 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.
+
+package encoding
+
+import (
+ "bytes"
+ "io"
+ "testing"
+
+ "golang.org/x/crypto/openpgp/errors"
+)
+
+var oidTests = []struct {
+ encoded []byte
+ bytes []byte
+ bitLength uint16
+ err error
+}{
+ {
+ encoded: []byte{0x1, 0x1},
+ bytes: []byte{0x1},
+ bitLength: 8,
+ },
+ {
+ encoded: []byte{0x2, 0x1, 0x2},
+ bytes: []byte{0x1, 0x2},
+ bitLength: 16,
+ },
+ {
+ encoded: []byte{0xa, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa},
+ bytes: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa},
+ bitLength: 80,
+ },
+ // extension overlap errors
+ {
+ encoded: []byte{0x0},
+ err: errors.UnsupportedError("reserved for future extensions"),
+ },
+ {
+ encoded: append([]byte{0xff}, make([]byte, 0xff)...),
+ err: errors.UnsupportedError("reserved for future extensions"),
+ },
+ // EOF error,
+ {
+ encoded: []byte{},
+ err: io.ErrUnexpectedEOF,
+ },
+ {
+ encoded: []byte{0xa},
+ err: io.ErrUnexpectedEOF,
+ },
+}
+
+func TestOID(t *testing.T) {
+ for i, test := range oidTests {
+ oid := new(OID)
+ if _, err := oid.ReadFrom(bytes.NewBuffer(test.encoded)); err != nil {
+ if !sameError(err, test.err) {
+ t.Errorf("#%d: ReadFrom error got:%q", i, err)
+ }
+ continue
+ }
+ if b := oid.Bytes(); !bytes.Equal(b, test.bytes) {
+ t.Errorf("#%d: bad creation got:%x want:%x", i, b, test.bytes)
+ }
+ if bl := oid.BitLength(); bl != test.bitLength {
+ t.Errorf("#%d: bad BitLength got:%d want:%d", i, bl, test.bitLength)
+ }
+ if b := oid.EncodedBytes(); !bytes.Equal(b, test.encoded) {
+ t.Errorf("#%d: bad encoding got:%x want:%x", i, b, test.encoded)
+ }
+ }
+}
+
+func sameError(err1, err2 error) bool {
+ switch {
+ case err1 == err2:
+ return true
+ case err1 == nil, err2 == nil:
+ return false
+ default:
+ return err1.Error() == err2.Error()
+ }
+}
diff --git a/openpgp/packet/encrypted_key.go b/openpgp/packet/encrypted_key.go
index 266840d..2b02f8e 100644
--- a/openpgp/packet/encrypted_key.go
+++ b/openpgp/packet/encrypted_key.go
@@ -13,6 +13,7 @@
"golang.org/x/crypto/openpgp/elgamal"
"golang.org/x/crypto/openpgp/errors"
+ "golang.org/x/crypto/openpgp/internal/encoding"
)
const encryptedKeyVersion = 3
@@ -25,7 +26,7 @@
CipherFunc CipherFunction // only valid after a successful Decrypt
Key []byte // only valid after a successful Decrypt
- encryptedMPI1, encryptedMPI2 parsedMPI
+ encryptedMPI1, encryptedMPI2 encoding.Field
}
func (e *EncryptedKey) parse(r io.Reader) (err error) {
@@ -41,13 +42,20 @@
e.Algo = PublicKeyAlgorithm(buf[9])
switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
- e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
- case PubKeyAlgoElGamal:
- e.encryptedMPI1.bytes, e.encryptedMPI1.bitLength, err = readMPI(r)
- if err != nil {
+ e.encryptedMPI1 = new(encoding.MPI)
+ if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
return
}
- e.encryptedMPI2.bytes, e.encryptedMPI2.bitLength, err = readMPI(r)
+ case PubKeyAlgoElGamal:
+ e.encryptedMPI1 = new(encoding.MPI)
+ if _, err = e.encryptedMPI1.ReadFrom(r); err != nil {
+ return
+ }
+
+ e.encryptedMPI2 = new(encoding.MPI)
+ if _, err = e.encryptedMPI2.ReadFrom(r); err != nil {
+ return
+ }
}
_, err = consumeAll(r)
return
@@ -72,10 +80,10 @@
// padding oracle attacks.
switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
- b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.bytes)
+ b, err = rsa.DecryptPKCS1v15(config.Random(), priv.PrivateKey.(*rsa.PrivateKey), e.encryptedMPI1.Bytes())
case PubKeyAlgoElGamal:
- c1 := new(big.Int).SetBytes(e.encryptedMPI1.bytes)
- c2 := new(big.Int).SetBytes(e.encryptedMPI2.bytes)
+ c1 := new(big.Int).SetBytes(e.encryptedMPI1.Bytes())
+ c2 := new(big.Int).SetBytes(e.encryptedMPI2.Bytes())
b, err = elgamal.Decrypt(priv.PrivateKey.(*elgamal.PrivateKey), c1, c2)
default:
err = errors.InvalidArgumentError("cannot decrypted encrypted session key with private key of type " + strconv.Itoa(int(priv.PubKeyAlgo)))
@@ -101,9 +109,9 @@
var mpiLen int
switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
- mpiLen = 2 + len(e.encryptedMPI1.bytes)
+ mpiLen = int(e.encryptedMPI1.EncodedLength())
case PubKeyAlgoElGamal:
- mpiLen = 2 + len(e.encryptedMPI1.bytes) + 2 + len(e.encryptedMPI2.bytes)
+ mpiLen = int(e.encryptedMPI1.EncodedLength()) + int(e.encryptedMPI2.EncodedLength())
default:
return errors.InvalidArgumentError("don't know how to serialize encrypted key type " + strconv.Itoa(int(e.Algo)))
}
@@ -116,9 +124,14 @@
switch e.Algo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly:
- writeMPIs(w, e.encryptedMPI1)
+ _, err := w.Write(e.encryptedMPI1.EncodedBytes())
+ return err
case PubKeyAlgoElGamal:
- writeMPIs(w, e.encryptedMPI1, e.encryptedMPI2)
+ if _, err := w.Write(e.encryptedMPI1.EncodedBytes()); err != nil {
+ return err
+ }
+ _, err := w.Write(e.encryptedMPI2.EncodedBytes())
+ return err
default:
panic("internal error")
}
@@ -170,7 +183,8 @@
if err != nil {
return err
}
- return writeMPI(w, 8*uint16(len(cipherText)), cipherText)
+ _, err = w.Write(encoding.NewMPI(cipherText).EncodedBytes())
+ return err
}
func serializeEncryptedKeyElGamal(w io.Writer, rand io.Reader, header [10]byte, pub *elgamal.PublicKey, keyBlock []byte) error {
@@ -191,9 +205,9 @@
if err != nil {
return err
}
- err = writeBig(w, c1)
- if err != nil {
+ if _, err = w.Write(new(encoding.MPI).SetBig(c1).EncodedBytes()); err != nil {
return err
}
- return writeBig(w, c2)
+ _, err = w.Write(new(encoding.MPI).SetBig(c2).EncodedBytes())
+ return err
}
diff --git a/openpgp/packet/packet.go b/openpgp/packet/packet.go
index 3eded93..6afb851 100644
--- a/openpgp/packet/packet.go
+++ b/openpgp/packet/packet.go
@@ -11,10 +11,10 @@
"crypto/aes"
"crypto/cipher"
"crypto/des"
+ "io"
+
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/openpgp/errors"
- "io"
- "math/big"
)
// readFull is the same as io.ReadFull except that reading zero bytes returns
@@ -487,44 +487,6 @@
return
}
-// readMPI reads a big integer from r. The bit length returned is the bit
-// length that was specified in r. This is preserved so that the integer can be
-// reserialized exactly.
-func readMPI(r io.Reader) (mpi []byte, bitLength uint16, err error) {
- var buf [2]byte
- _, err = readFull(r, buf[0:])
- if err != nil {
- return
- }
- bitLength = uint16(buf[0])<<8 | uint16(buf[1])
- numBytes := (int(bitLength) + 7) / 8
- mpi = make([]byte, numBytes)
- _, err = readFull(r, mpi)
- return
-}
-
-// mpiLength returns the length of the given *big.Int when serialized as an
-// MPI.
-func mpiLength(n *big.Int) (mpiLengthInBytes int) {
- mpiLengthInBytes = 2 /* MPI length */
- mpiLengthInBytes += (n.BitLen() + 7) / 8
- return
-}
-
-// writeMPI serializes a big integer to w.
-func writeMPI(w io.Writer, bitLength uint16, mpiBytes []byte) (err error) {
- _, err = w.Write([]byte{byte(bitLength >> 8), byte(bitLength)})
- if err == nil {
- _, err = w.Write(mpiBytes)
- }
- return
-}
-
-// writeBig serializes a *big.Int to w.
-func writeBig(w io.Writer, i *big.Int) error {
- return writeMPI(w, uint16(i.BitLen()), i.Bytes())
-}
-
// CompressionAlgo Represents the different compression algorithms
// supported by OpenPGP (except for BZIP2, which is not currently
// supported). See Section 9.3 of RFC 4880.
diff --git a/openpgp/packet/private_key.go b/openpgp/packet/private_key.go
index 34734cc..2e66b4b 100644
--- a/openpgp/packet/private_key.go
+++ b/openpgp/packet/private_key.go
@@ -20,6 +20,7 @@
"golang.org/x/crypto/openpgp/elgamal"
"golang.org/x/crypto/openpgp/errors"
+ "golang.org/x/crypto/openpgp/internal/encoding"
"golang.org/x/crypto/openpgp/s2k"
)
@@ -204,31 +205,32 @@
}
func serializeRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error {
- err := writeBig(w, priv.D)
- if err != nil {
+ if _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes()); err != nil {
return err
}
- err = writeBig(w, priv.Primes[1])
- if err != nil {
+ if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[1]).EncodedBytes()); err != nil {
return err
}
- err = writeBig(w, priv.Primes[0])
- if err != nil {
+ if _, err := w.Write(new(encoding.MPI).SetBig(priv.Primes[0]).EncodedBytes()); err != nil {
return err
}
- return writeBig(w, priv.Precomputed.Qinv)
+ _, err := w.Write(new(encoding.MPI).SetBig(priv.Precomputed.Qinv).EncodedBytes())
+ return err
}
func serializeDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error {
- return writeBig(w, priv.X)
+ _, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
+ return err
}
func serializeElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
- return writeBig(w, priv.X)
+ _, err := w.Write(new(encoding.MPI).SetBig(priv.X).EncodedBytes())
+ return err
}
func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
- return writeBig(w, priv.D)
+ _, err := w.Write(new(encoding.MPI).SetBig(priv.D).EncodedBytes())
+ return err
}
// Decrypt decrypts an encrypted private key using a passphrase.
@@ -294,23 +296,25 @@
rsaPriv.PublicKey = *rsaPub
buf := bytes.NewBuffer(data)
- d, _, err := readMPI(buf)
- if err != nil {
- return
- }
- p, _, err := readMPI(buf)
- if err != nil {
- return
- }
- q, _, err := readMPI(buf)
- if err != nil {
- return
+ d := new(encoding.MPI)
+ if _, err := d.ReadFrom(buf); err != nil {
+ return err
}
- rsaPriv.D = new(big.Int).SetBytes(d)
+ p := new(encoding.MPI)
+ if _, err := p.ReadFrom(buf); err != nil {
+ return err
+ }
+
+ q := new(encoding.MPI)
+ if _, err := q.ReadFrom(buf); err != nil {
+ return err
+ }
+
+ rsaPriv.D = new(big.Int).SetBytes(d.Bytes())
rsaPriv.Primes = make([]*big.Int, 2)
- rsaPriv.Primes[0] = new(big.Int).SetBytes(p)
- rsaPriv.Primes[1] = new(big.Int).SetBytes(q)
+ rsaPriv.Primes[0] = new(big.Int).SetBytes(p.Bytes())
+ rsaPriv.Primes[1] = new(big.Int).SetBytes(q.Bytes())
if err := rsaPriv.Validate(); err != nil {
return err
}
@@ -328,12 +332,12 @@
dsaPriv.PublicKey = *dsaPub
buf := bytes.NewBuffer(data)
- x, _, err := readMPI(buf)
- if err != nil {
- return
+ x := new(encoding.MPI)
+ if _, err := x.ReadFrom(buf); err != nil {
+ return err
}
- dsaPriv.X = new(big.Int).SetBytes(x)
+ dsaPriv.X = new(big.Int).SetBytes(x.Bytes())
pk.PrivateKey = dsaPriv
pk.Encrypted = false
pk.encryptedData = nil
@@ -347,12 +351,12 @@
priv.PublicKey = *pub
buf := bytes.NewBuffer(data)
- x, _, err := readMPI(buf)
- if err != nil {
- return
+ x := new(encoding.MPI)
+ if _, err := x.ReadFrom(buf); err != nil {
+ return err
}
- priv.X = new(big.Int).SetBytes(x)
+ priv.X = new(big.Int).SetBytes(x.Bytes())
pk.PrivateKey = priv
pk.Encrypted = false
pk.encryptedData = nil
@@ -362,17 +366,17 @@
func (pk *PrivateKey) parseECDSAPrivateKey(data []byte) (err error) {
ecdsaPub := pk.PublicKey.PublicKey.(*ecdsa.PublicKey)
+ ecdsaPriv := new(ecdsa.PrivateKey)
+ ecdsaPriv.PublicKey = *ecdsaPub
buf := bytes.NewBuffer(data)
- d, _, err := readMPI(buf)
- if err != nil {
- return
+ d := new(encoding.MPI)
+ if _, err := d.ReadFrom(buf); err != nil {
+ return err
}
- pk.PrivateKey = &ecdsa.PrivateKey{
- PublicKey: *ecdsaPub,
- D: new(big.Int).SetBytes(d),
- }
+ ecdsaPriv.D = new(big.Int).SetBytes(d.Bytes())
+ pk.PrivateKey = ecdsaPriv
pk.Encrypted = false
pk.encryptedData = nil
diff --git a/openpgp/packet/public_key.go b/openpgp/packet/public_key.go
index ead2623..03ba846 100644
--- a/openpgp/packet/public_key.go
+++ b/openpgp/packet/public_key.go
@@ -24,6 +24,7 @@
"golang.org/x/crypto/openpgp/elgamal"
"golang.org/x/crypto/openpgp/errors"
+ "golang.org/x/crypto/openpgp/internal/encoding"
)
var (
@@ -35,120 +36,9 @@
oidCurveP521 []byte = []byte{0x2B, 0x81, 0x04, 0x00, 0x23}
)
-const maxOIDLength = 8
-
-// ecdsaKey stores the algorithm-specific fields for ECDSA keys.
-// as defined in RFC 6637, Section 9.
-type ecdsaKey struct {
- // oid contains the OID byte sequence identifying the elliptic curve used
- oid []byte
- // p contains the elliptic curve point that represents the public key
- p parsedMPI
-}
-
-// parseOID reads the OID for the curve as defined in RFC 6637, Section 9.
-func parseOID(r io.Reader) (oid []byte, err error) {
- buf := make([]byte, maxOIDLength)
- if _, err = readFull(r, buf[:1]); err != nil {
- return
- }
- oidLen := buf[0]
- if int(oidLen) > len(buf) {
- err = errors.UnsupportedError("invalid oid length: " + strconv.Itoa(int(oidLen)))
- return
- }
- oid = buf[:oidLen]
- _, err = readFull(r, oid)
- return
-}
-
-func (f *ecdsaKey) parse(r io.Reader) (err error) {
- if f.oid, err = parseOID(r); err != nil {
- return err
- }
- f.p.bytes, f.p.bitLength, err = readMPI(r)
- return
-}
-
-func (f *ecdsaKey) serialize(w io.Writer) (err error) {
- buf := make([]byte, maxOIDLength+1)
- buf[0] = byte(len(f.oid))
- copy(buf[1:], f.oid)
- if _, err = w.Write(buf[:len(f.oid)+1]); err != nil {
- return
- }
- return writeMPIs(w, f.p)
-}
-
-func (f *ecdsaKey) newECDSA() (*ecdsa.PublicKey, error) {
- var c elliptic.Curve
- if bytes.Equal(f.oid, oidCurveP256) {
- c = elliptic.P256()
- } else if bytes.Equal(f.oid, oidCurveP384) {
- c = elliptic.P384()
- } else if bytes.Equal(f.oid, oidCurveP521) {
- c = elliptic.P521()
- } else {
- return nil, errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", f.oid))
- }
- x, y := elliptic.Unmarshal(c, f.p.bytes)
- if x == nil {
- return nil, errors.UnsupportedError("failed to parse EC point")
- }
- return &ecdsa.PublicKey{Curve: c, X: x, Y: y}, nil
-}
-
-func (f *ecdsaKey) byteLen() int {
- return 1 + len(f.oid) + 2 + len(f.p.bytes)
-}
-
type kdfHashFunction byte
type kdfAlgorithm byte
-// ecdhKdf stores key derivation function parameters
-// used for ECDH encryption. See RFC 6637, Section 9.
-type ecdhKdf struct {
- KdfHash kdfHashFunction
- KdfAlgo kdfAlgorithm
-}
-
-func (f *ecdhKdf) parse(r io.Reader) (err error) {
- buf := make([]byte, 1)
- if _, err = readFull(r, buf); err != nil {
- return
- }
- kdfLen := int(buf[0])
- if kdfLen < 3 {
- return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen))
- }
- buf = make([]byte, kdfLen)
- if _, err = readFull(r, buf); err != nil {
- return
- }
- reserved := int(buf[0])
- f.KdfHash = kdfHashFunction(buf[1])
- f.KdfAlgo = kdfAlgorithm(buf[2])
- if reserved != 0x01 {
- return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(reserved))
- }
- return
-}
-
-func (f *ecdhKdf) serialize(w io.Writer) (err error) {
- buf := make([]byte, 4)
- // See RFC 6637, Section 9, Algorithm-Specific Fields for ECDH keys.
- buf[0] = byte(0x03) // Length of the following fields
- buf[1] = byte(0x01) // Reserved for future extensions, must be 1 for now
- buf[2] = byte(f.KdfHash)
- buf[3] = byte(f.KdfAlgo)
- _, err = w.Write(buf[:])
- return
-}
-
-func (f *ecdhKdf) byteLen() int {
- return 4
-}
-
// PublicKey represents an OpenPGP public key. See RFC 4880, section 5.5.2.
type PublicKey struct {
CreationTime time.Time
@@ -158,11 +48,19 @@
KeyId uint64
IsSubkey bool
- n, e, p, q, g, y parsedMPI
+ // RFC 4880 fields
+ n, e, p, q, g, y encoding.Field
// RFC 6637 fields
- ec *ecdsaKey
- ecdh *ecdhKdf
+ // oid contains the OID byte sequence identifying the elliptic curve used
+ oid encoding.Field
+
+ // kdf stores key derivation function parameters
+ // used for ECDH encryption. See RFC 6637, Section 9.
+ kdf encoding.Field
+
+ kdfHash kdfHashFunction
+ kdfAlgo kdfAlgorithm
}
// signingKey provides a convenient abstraction over signature verification
@@ -172,21 +70,14 @@
serializeWithoutHeaders(io.Writer) error
}
-func fromBig(n *big.Int) parsedMPI {
- return parsedMPI{
- bytes: n.Bytes(),
- bitLength: uint16(n.BitLen()),
- }
-}
-
// NewRSAPublicKey returns a PublicKey that wraps the given rsa.PublicKey.
func NewRSAPublicKey(creationTime time.Time, pub *rsa.PublicKey) *PublicKey {
pk := &PublicKey{
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoRSA,
PublicKey: pub,
- n: fromBig(pub.N),
- e: fromBig(big.NewInt(int64(pub.E))),
+ n: new(encoding.MPI).SetBig(pub.N),
+ e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))),
}
pk.setFingerPrintAndKeyId()
@@ -199,10 +90,10 @@
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoDSA,
PublicKey: pub,
- p: fromBig(pub.P),
- q: fromBig(pub.Q),
- g: fromBig(pub.G),
- y: fromBig(pub.Y),
+ p: new(encoding.MPI).SetBig(pub.P),
+ q: new(encoding.MPI).SetBig(pub.Q),
+ g: new(encoding.MPI).SetBig(pub.G),
+ y: new(encoding.MPI).SetBig(pub.Y),
}
pk.setFingerPrintAndKeyId()
@@ -215,9 +106,9 @@
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoElGamal,
PublicKey: pub,
- p: fromBig(pub.P),
- g: fromBig(pub.G),
- y: fromBig(pub.Y),
+ p: new(encoding.MPI).SetBig(pub.P),
+ g: new(encoding.MPI).SetBig(pub.G),
+ y: new(encoding.MPI).SetBig(pub.Y),
}
pk.setFingerPrintAndKeyId()
@@ -229,23 +120,20 @@
CreationTime: creationTime,
PubKeyAlgo: PubKeyAlgoECDSA,
PublicKey: pub,
- ec: new(ecdsaKey),
+ p: encoding.NewMPI(elliptic.Marshal(pub.Curve, pub.X, pub.Y)),
}
switch pub.Curve {
case elliptic.P256():
- pk.ec.oid = oidCurveP256
+ pk.oid = encoding.NewOID(oidCurveP256)
case elliptic.P384():
- pk.ec.oid = oidCurveP384
+ pk.oid = encoding.NewOID(oidCurveP384)
case elliptic.P521():
- pk.ec.oid = oidCurveP521
+ pk.oid = encoding.NewOID(oidCurveP521)
default:
panic("unknown elliptic curve")
}
- pk.ec.p.bytes = elliptic.Marshal(pub.Curve, pub.X, pub.Y)
- pk.ec.p.bitLength = uint16(8 * len(pk.ec.p.bytes))
-
pk.setFingerPrintAndKeyId()
return pk
}
@@ -270,22 +158,9 @@
case PubKeyAlgoElGamal:
err = pk.parseElGamal(r)
case PubKeyAlgoECDSA:
- pk.ec = new(ecdsaKey)
- if err = pk.ec.parse(r); err != nil {
- return err
- }
- pk.PublicKey, err = pk.ec.newECDSA()
+ err = pk.parseECDSA(r)
case PubKeyAlgoECDH:
- pk.ec = new(ecdsaKey)
- if err = pk.ec.parse(r); err != nil {
- return
- }
- pk.ecdh = new(ecdhKdf)
- if err = pk.ecdh.parse(r); err != nil {
- return
- }
- // The ECDH key is stored in an ecdsa.PublicKey for convenience.
- pk.PublicKey, err = pk.ec.newECDSA()
+ err = pk.parseECDH(r)
default:
err = errors.UnsupportedError("public key type: " + strconv.Itoa(int(pk.PubKeyAlgo)))
}
@@ -309,26 +184,26 @@
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
// section 5.5.2.
func (pk *PublicKey) parseRSA(r io.Reader) (err error) {
- pk.n.bytes, pk.n.bitLength, err = readMPI(r)
- if err != nil {
+ pk.n = new(encoding.MPI)
+ if _, err = pk.n.ReadFrom(r); err != nil {
return
}
- pk.e.bytes, pk.e.bitLength, err = readMPI(r)
- if err != nil {
+ pk.e = new(encoding.MPI)
+ if _, err = pk.e.ReadFrom(r); err != nil {
return
}
- if len(pk.e.bytes) > 3 {
+ if len(pk.e.Bytes()) > 3 {
err = errors.UnsupportedError("large public exponent")
return
}
rsa := &rsa.PublicKey{
- N: new(big.Int).SetBytes(pk.n.bytes),
+ N: new(big.Int).SetBytes(pk.n.Bytes()),
E: 0,
}
- for i := 0; i < len(pk.e.bytes); i++ {
+ for i := 0; i < len(pk.e.Bytes()); i++ {
rsa.E <<= 8
- rsa.E |= int(pk.e.bytes[i])
+ rsa.E |= int(pk.e.Bytes()[i])
}
pk.PublicKey = rsa
return
@@ -337,28 +212,28 @@
// parseDSA parses DSA public key material from the given Reader. See RFC 4880,
// section 5.5.2.
func (pk *PublicKey) parseDSA(r io.Reader) (err error) {
- pk.p.bytes, pk.p.bitLength, err = readMPI(r)
- if err != nil {
+ pk.p = new(encoding.MPI)
+ if _, err = pk.p.ReadFrom(r); err != nil {
return
}
- pk.q.bytes, pk.q.bitLength, err = readMPI(r)
- if err != nil {
+ pk.q = new(encoding.MPI)
+ if _, err = pk.q.ReadFrom(r); err != nil {
return
}
- pk.g.bytes, pk.g.bitLength, err = readMPI(r)
- if err != nil {
+ pk.g = new(encoding.MPI)
+ if _, err = pk.g.ReadFrom(r); err != nil {
return
}
- pk.y.bytes, pk.y.bitLength, err = readMPI(r)
- if err != nil {
+ pk.y = new(encoding.MPI)
+ if _, err = pk.y.ReadFrom(r); err != nil {
return
}
dsa := new(dsa.PublicKey)
- dsa.P = new(big.Int).SetBytes(pk.p.bytes)
- dsa.Q = new(big.Int).SetBytes(pk.q.bytes)
- dsa.G = new(big.Int).SetBytes(pk.g.bytes)
- dsa.Y = new(big.Int).SetBytes(pk.y.bytes)
+ dsa.P = new(big.Int).SetBytes(pk.p.Bytes())
+ dsa.Q = new(big.Int).SetBytes(pk.q.Bytes())
+ dsa.G = new(big.Int).SetBytes(pk.g.Bytes())
+ dsa.Y = new(big.Int).SetBytes(pk.y.Bytes())
pk.PublicKey = dsa
return
}
@@ -366,27 +241,79 @@
// parseElGamal parses ElGamal public key material from the given Reader. See
// RFC 4880, section 5.5.2.
func (pk *PublicKey) parseElGamal(r io.Reader) (err error) {
- pk.p.bytes, pk.p.bitLength, err = readMPI(r)
- if err != nil {
+ pk.p = new(encoding.MPI)
+ if _, err = pk.p.ReadFrom(r); err != nil {
return
}
- pk.g.bytes, pk.g.bitLength, err = readMPI(r)
- if err != nil {
+ pk.g = new(encoding.MPI)
+ if _, err = pk.g.ReadFrom(r); err != nil {
return
}
- pk.y.bytes, pk.y.bitLength, err = readMPI(r)
- if err != nil {
+ pk.y = new(encoding.MPI)
+ if _, err = pk.y.ReadFrom(r); err != nil {
return
}
elgamal := new(elgamal.PublicKey)
- elgamal.P = new(big.Int).SetBytes(pk.p.bytes)
- elgamal.G = new(big.Int).SetBytes(pk.g.bytes)
- elgamal.Y = new(big.Int).SetBytes(pk.y.bytes)
+ elgamal.P = new(big.Int).SetBytes(pk.p.Bytes())
+ elgamal.G = new(big.Int).SetBytes(pk.g.Bytes())
+ elgamal.Y = new(big.Int).SetBytes(pk.y.Bytes())
pk.PublicKey = elgamal
return
}
+// parseECDSA parses ECDSA public key material from the given Reader. See
+// RFC 6637, Section 9.
+func (pk *PublicKey) parseECDSA(r io.Reader) (err error) {
+ pk.oid = new(encoding.OID)
+ if _, err = pk.oid.ReadFrom(r); err != nil {
+ return
+ }
+ pk.p = new(encoding.MPI)
+ if _, err = pk.p.ReadFrom(r); err != nil {
+ return
+ }
+
+ var c elliptic.Curve
+ if bytes.Equal(pk.oid.Bytes(), oidCurveP256) {
+ c = elliptic.P256()
+ } else if bytes.Equal(pk.oid.Bytes(), oidCurveP384) {
+ c = elliptic.P384()
+ } else if bytes.Equal(pk.oid.Bytes(), oidCurveP521) {
+ c = elliptic.P521()
+ } else {
+ return errors.UnsupportedError(fmt.Sprintf("unsupported oid: %x", pk.oid))
+ }
+ x, y := elliptic.Unmarshal(c, pk.p.Bytes())
+ if x == nil {
+ return errors.UnsupportedError("failed to parse EC point")
+ }
+ pk.PublicKey = &ecdsa.PublicKey{Curve: c, X: x, Y: y}
+ return
+}
+
+// parseECDH parses ECDH public key material from the given Reader. See
+// RFC 6637, Section 9.
+func (pk *PublicKey) parseECDH(r io.Reader) (err error) {
+ if err = pk.parseECDSA(r); err != nil {
+ return
+ }
+
+ pk.kdf = new(encoding.OID)
+ if _, err = pk.kdf.ReadFrom(r); err != nil {
+ return
+ }
+ if kdfLen := len(pk.kdf.Bytes()); kdfLen < 3 {
+ return errors.UnsupportedError("Unsupported ECDH KDF length: " + strconv.Itoa(kdfLen))
+ }
+ if reserved := pk.kdf.Bytes()[0]; reserved != 0x01 {
+ return errors.UnsupportedError("Unsupported KDF reserved field: " + strconv.Itoa(int(reserved)))
+ }
+ pk.kdfHash = kdfHashFunction(pk.kdf.Bytes()[1])
+ pk.kdfAlgo = kdfAlgorithm(pk.kdf.Bytes()[2])
+ return
+}
+
// SerializeSignaturePrefix writes the prefix for this public key to the given Writer.
// The prefix is used when calculating a signature over this public key. See
// RFC 4880, section 5.2.4.
@@ -394,22 +321,24 @@
var pLength uint16
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- pLength += 2 + uint16(len(pk.n.bytes))
- pLength += 2 + uint16(len(pk.e.bytes))
+ pLength += pk.n.EncodedLength()
+ pLength += pk.e.EncodedLength()
case PubKeyAlgoDSA:
- pLength += 2 + uint16(len(pk.p.bytes))
- pLength += 2 + uint16(len(pk.q.bytes))
- pLength += 2 + uint16(len(pk.g.bytes))
- pLength += 2 + uint16(len(pk.y.bytes))
+ pLength += pk.p.EncodedLength()
+ pLength += pk.q.EncodedLength()
+ pLength += pk.g.EncodedLength()
+ pLength += pk.y.EncodedLength()
case PubKeyAlgoElGamal:
- pLength += 2 + uint16(len(pk.p.bytes))
- pLength += 2 + uint16(len(pk.g.bytes))
- pLength += 2 + uint16(len(pk.y.bytes))
+ pLength += pk.p.EncodedLength()
+ pLength += pk.g.EncodedLength()
+ pLength += pk.y.EncodedLength()
case PubKeyAlgoECDSA:
- pLength += uint16(pk.ec.byteLen())
+ pLength += pk.oid.EncodedLength()
+ pLength += pk.p.EncodedLength()
case PubKeyAlgoECDH:
- pLength += uint16(pk.ec.byteLen())
- pLength += uint16(pk.ecdh.byteLen())
+ pLength += pk.oid.EncodedLength()
+ pLength += pk.p.EncodedLength()
+ pLength += pk.kdf.EncodedLength()
default:
panic("unknown public key algorithm")
}
@@ -423,22 +352,24 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- length += 2 + len(pk.n.bytes)
- length += 2 + len(pk.e.bytes)
+ length += int(pk.n.EncodedLength())
+ length += int(pk.e.EncodedLength())
case PubKeyAlgoDSA:
- length += 2 + len(pk.p.bytes)
- length += 2 + len(pk.q.bytes)
- length += 2 + len(pk.g.bytes)
- length += 2 + len(pk.y.bytes)
+ length += int(pk.p.EncodedLength())
+ length += int(pk.q.EncodedLength())
+ length += int(pk.g.EncodedLength())
+ length += int(pk.y.EncodedLength())
case PubKeyAlgoElGamal:
- length += 2 + len(pk.p.bytes)
- length += 2 + len(pk.g.bytes)
- length += 2 + len(pk.y.bytes)
+ length += int(pk.p.EncodedLength())
+ length += int(pk.g.EncodedLength())
+ length += int(pk.y.EncodedLength())
case PubKeyAlgoECDSA:
- length += pk.ec.byteLen()
+ length += int(pk.oid.EncodedLength())
+ length += int(pk.p.EncodedLength())
case PubKeyAlgoECDH:
- length += pk.ec.byteLen()
- length += pk.ecdh.byteLen()
+ length += int(pk.oid.EncodedLength())
+ length += int(pk.p.EncodedLength())
+ length += int(pk.kdf.EncodedLength())
default:
panic("unknown public key algorithm")
}
@@ -473,18 +404,47 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- return writeMPIs(w, pk.n, pk.e)
- case PubKeyAlgoDSA:
- return writeMPIs(w, pk.p, pk.q, pk.g, pk.y)
- case PubKeyAlgoElGamal:
- return writeMPIs(w, pk.p, pk.g, pk.y)
- case PubKeyAlgoECDSA:
- return pk.ec.serialize(w)
- case PubKeyAlgoECDH:
- if err = pk.ec.serialize(w); err != nil {
+ if _, err = w.Write(pk.n.EncodedBytes()); err != nil {
return
}
- return pk.ecdh.serialize(w)
+ _, err = w.Write(pk.e.EncodedBytes())
+ return
+ case PubKeyAlgoDSA:
+ if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
+ return
+ }
+ if _, err = w.Write(pk.q.EncodedBytes()); err != nil {
+ return
+ }
+ if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(pk.y.EncodedBytes())
+ return
+ case PubKeyAlgoElGamal:
+ if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
+ return
+ }
+ if _, err = w.Write(pk.g.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(pk.y.EncodedBytes())
+ return
+ case PubKeyAlgoECDSA:
+ if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(pk.p.EncodedBytes())
+ return
+ case PubKeyAlgoECDH:
+ if _, err = w.Write(pk.oid.EncodedBytes()); err != nil {
+ return
+ }
+ if _, err = w.Write(pk.p.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(pk.kdf.EncodedBytes())
+ return
}
return errors.InvalidArgumentError("bad public-key algorithm")
}
@@ -515,7 +475,7 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey, _ := pk.PublicKey.(*rsa.PublicKey)
- err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes)
+ err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.Bytes())
if err != nil {
return errors.SignatureError("RSA verification failure")
}
@@ -527,13 +487,13 @@
if len(hashBytes) > subgroupSize {
hashBytes = hashBytes[:subgroupSize]
}
- if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) {
+ if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) {
return errors.SignatureError("DSA verification failure")
}
return nil
case PubKeyAlgoECDSA:
ecdsaPublicKey := pk.PublicKey.(*ecdsa.PublicKey)
- if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.bytes), new(big.Int).SetBytes(sig.ECDSASigS.bytes)) {
+ if !ecdsa.Verify(ecdsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.ECDSASigR.Bytes()), new(big.Int).SetBytes(sig.ECDSASigS.Bytes())) {
return errors.SignatureError("ECDSA verification failure")
}
return nil
@@ -566,7 +526,7 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
rsaPublicKey := pk.PublicKey.(*rsa.PublicKey)
- if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil {
+ if err = rsa.VerifyPKCS1v15(rsaPublicKey, sig.Hash, hashBytes, sig.RSASignature.Bytes()); err != nil {
return errors.SignatureError("RSA verification failure")
}
return
@@ -577,7 +537,7 @@
if len(hashBytes) > subgroupSize {
hashBytes = hashBytes[:subgroupSize]
}
- if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.bytes), new(big.Int).SetBytes(sig.DSASigS.bytes)) {
+ if !dsa.Verify(dsaPublicKey, hashBytes, new(big.Int).SetBytes(sig.DSASigR.Bytes()), new(big.Int).SetBytes(sig.DSASigS.Bytes())) {
return errors.SignatureError("DSA verification failure")
}
return nil
@@ -712,35 +672,15 @@
return fmt.Sprintf("%X", pk.Fingerprint[16:20])
}
-// A parsedMPI is used to store the contents of a big integer, along with the
-// bit length that was specified in the original input. This allows the MPI to
-// be reserialized exactly.
-type parsedMPI struct {
- bytes []byte
- bitLength uint16
-}
-
-// writeMPIs is a utility function for serializing several big integers to the
-// given Writer.
-func writeMPIs(w io.Writer, mpis ...parsedMPI) (err error) {
- for _, mpi := range mpis {
- err = writeMPI(w, mpi.bitLength, mpi.bytes)
- if err != nil {
- return
- }
- }
- return
-}
-
// BitLength returns the bit length for the given public key.
func (pk *PublicKey) BitLength() (bitLength uint16, err error) {
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- bitLength = pk.n.bitLength
+ bitLength = pk.n.BitLength()
case PubKeyAlgoDSA:
- bitLength = pk.p.bitLength
+ bitLength = pk.p.BitLength()
case PubKeyAlgoElGamal:
- bitLength = pk.p.bitLength
+ bitLength = pk.p.BitLength()
default:
err = errors.InvalidArgumentError("bad public-key algorithm")
}
diff --git a/openpgp/packet/public_key_test.go b/openpgp/packet/public_key_test.go
index 7ad7d91..6cc732a 100644
--- a/openpgp/packet/public_key_test.go
+++ b/openpgp/packet/public_key_test.go
@@ -101,11 +101,11 @@
t.Error(err)
}
pubkey := p.(*PublicKey)
- if !bytes.Equal(pubkey.ec.oid, []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) {
- t.Errorf("Unexpected pubkey OID: %x", pubkey.ec.oid)
+ if !bytes.Equal(pubkey.oid.Bytes(), []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) {
+ t.Errorf("Unexpected pubkey OID: %x", pubkey.oid.Bytes())
}
- if !bytes.Equal(pubkey.ec.p.bytes[:5], []byte{0x04, 0xf6, 0xb8, 0xc5, 0xac}) {
- t.Errorf("Unexpected pubkey P[:5]: %x", pubkey.ec.p.bytes)
+ if !bytes.Equal(pubkey.p.Bytes()[:5], []byte{0x04, 0xf6, 0xb8, 0xc5, 0xac}) {
+ t.Errorf("Unexpected pubkey P[:5]: %x", pubkey.p.Bytes())
}
if pubkey.KeyId != 0x098033880F54719F {
t.Errorf("Unexpected pubkey ID: %x", pubkey.KeyId)
@@ -147,17 +147,17 @@
t.Error(err)
}
subkey := p.(*PublicKey)
- if !bytes.Equal(subkey.ec.oid, []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) {
- t.Errorf("Unexpected subkey OID: %x", subkey.ec.oid)
+ if !bytes.Equal(subkey.oid.Bytes(), []byte{0x2b, 0x81, 0x04, 0x00, 0x22}) {
+ t.Errorf("Unexpected subkey OID: %x", subkey.oid.Bytes())
}
- if !bytes.Equal(subkey.ec.p.bytes[:5], []byte{0x04, 0x2f, 0xaa, 0x84, 0x02}) {
- t.Errorf("Unexpected subkey P[:5]: %x", subkey.ec.p.bytes)
+ if !bytes.Equal(subkey.p.Bytes()[:5], []byte{0x04, 0x2f, 0xaa, 0x84, 0x02}) {
+ t.Errorf("Unexpected subkey P[:5]: %x", subkey.p.Bytes())
}
- if subkey.ecdh.KdfHash != 0x09 {
- t.Error("Expected KDF hash function SHA384 (0x09), got", subkey.ecdh.KdfHash)
+ if subkey.kdfHash != 0x09 {
+ t.Error("Expected KDF hash function SHA384 (0x09), got", subkey.kdfHash)
}
- if subkey.ecdh.KdfAlgo != 0x09 {
- t.Error("Expected KDF symmetric alg AES256 (0x09), got", subkey.ecdh.KdfAlgo)
+ if subkey.kdfAlgo != 0x09 {
+ t.Error("Expected KDF symmetric alg AES256 (0x09), got", subkey.kdfAlgo)
}
if subkey.KeyId != 0xAA8B938F9A201946 {
t.Errorf("Unexpected subkey ID: %x", subkey.KeyId)
diff --git a/openpgp/packet/public_key_v3.go b/openpgp/packet/public_key_v3.go
index 5daf7b6..1f52951 100644
--- a/openpgp/packet/public_key_v3.go
+++ b/openpgp/packet/public_key_v3.go
@@ -17,6 +17,7 @@
"time"
"golang.org/x/crypto/openpgp/errors"
+ "golang.org/x/crypto/openpgp/internal/encoding"
)
// PublicKeyV3 represents older, version 3 public keys. These keys are less secure and
@@ -32,7 +33,7 @@
KeyId uint64
IsSubkey bool
- n, e parsedMPI
+ n, e encoding.Field
}
// newRSAPublicKeyV3 returns a PublicKey that wraps the given rsa.PublicKey.
@@ -42,8 +43,8 @@
pk := &PublicKeyV3{
CreationTime: creationTime,
PublicKey: pub,
- n: fromBig(pub.N),
- e: fromBig(big.NewInt(int64(pub.E))),
+ n: new(encoding.MPI).SetBig(pub.N),
+ e: new(encoding.MPI).SetBig(big.NewInt(int64(pub.E))),
}
pk.setFingerPrintAndKeyId()
@@ -79,35 +80,37 @@
func (pk *PublicKeyV3) setFingerPrintAndKeyId() {
// RFC 4880, section 12.2
fingerPrint := md5.New()
- fingerPrint.Write(pk.n.bytes)
- fingerPrint.Write(pk.e.bytes)
+ fingerPrint.Write(pk.n.Bytes())
+ fingerPrint.Write(pk.e.Bytes())
fingerPrint.Sum(pk.Fingerprint[:0])
- pk.KeyId = binary.BigEndian.Uint64(pk.n.bytes[len(pk.n.bytes)-8:])
+ pk.KeyId = binary.BigEndian.Uint64(pk.n.Bytes()[len(pk.n.Bytes())-8:])
}
// parseRSA parses RSA public key material from the given Reader. See RFC 4880,
// section 5.5.2.
func (pk *PublicKeyV3) parseRSA(r io.Reader) (err error) {
- if pk.n.bytes, pk.n.bitLength, err = readMPI(r); err != nil {
+ pk.n = new(encoding.MPI)
+ if _, err = pk.n.ReadFrom(r); err != nil {
return
}
- if pk.e.bytes, pk.e.bitLength, err = readMPI(r); err != nil {
+ pk.e = new(encoding.MPI)
+ if _, err = pk.e.ReadFrom(r); err != nil {
return
}
// RFC 4880 Section 12.2 requires the low 8 bytes of the
// modulus to form the key id.
- if len(pk.n.bytes) < 8 {
+ if len(pk.n.Bytes()) < 8 {
return errors.StructuralError("v3 public key modulus is too short")
}
- if len(pk.e.bytes) > 3 {
+ if len(pk.e.Bytes()) > 3 {
err = errors.UnsupportedError("large public exponent")
return
}
- rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.bytes)}
- for i := 0; i < len(pk.e.bytes); i++ {
+ rsa := &rsa.PublicKey{N: new(big.Int).SetBytes(pk.n.Bytes())}
+ for i := 0; i < len(pk.e.Bytes()); i++ {
rsa.E <<= 8
- rsa.E |= int(pk.e.bytes[i])
+ rsa.E |= int(pk.e.Bytes()[i])
}
pk.PublicKey = rsa
return
@@ -120,8 +123,8 @@
var pLength uint16
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- pLength += 2 + uint16(len(pk.n.bytes))
- pLength += 2 + uint16(len(pk.e.bytes))
+ pLength += pk.n.EncodedLength()
+ pLength += pk.e.EncodedLength()
default:
panic("unknown public key algorithm")
}
@@ -135,8 +138,8 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- length += 2 + len(pk.n.bytes)
- length += 2 + len(pk.e.bytes)
+ length += int(pk.n.EncodedLength())
+ length += int(pk.e.EncodedLength())
default:
panic("unknown public key algorithm")
}
@@ -175,7 +178,11 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- return writeMPIs(w, pk.n, pk.e)
+ if _, err = w.Write(pk.n.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(pk.e.EncodedBytes())
+ return
}
return errors.InvalidArgumentError("bad public-key algorithm")
}
@@ -208,7 +215,7 @@
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.bytes); err != nil {
+ if err = rsa.VerifyPKCS1v15(pk.PublicKey, sig.Hash, hashBytes, sig.RSASignature.Bytes()); err != nil {
return errors.SignatureError("RSA verification failure")
}
return
@@ -271,7 +278,7 @@
func (pk *PublicKeyV3) BitLength() (bitLength uint16, err error) {
switch pk.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSAEncryptOnly, PubKeyAlgoRSASignOnly:
- bitLength = pk.n.bitLength
+ bitLength = pk.n.BitLength()
default:
err = errors.InvalidArgumentError("bad public-key algorithm")
}
diff --git a/openpgp/packet/signature.go b/openpgp/packet/signature.go
index 6ce0cbe..828f021 100644
--- a/openpgp/packet/signature.go
+++ b/openpgp/packet/signature.go
@@ -18,6 +18,7 @@
"time"
"golang.org/x/crypto/openpgp/errors"
+ "golang.org/x/crypto/openpgp/internal/encoding"
"golang.org/x/crypto/openpgp/s2k"
)
@@ -42,9 +43,9 @@
HashTag [2]byte
CreationTime time.Time
- RSASignature parsedMPI
- DSASigR, DSASigS parsedMPI
- ECDSASigR, ECDSASigS parsedMPI
+ RSASignature encoding.Field
+ DSASigR, DSASigS encoding.Field
+ ECDSASigR, ECDSASigS encoding.Field
// rawSubpackets contains the unparsed subpackets, in order.
rawSubpackets []outputSubpacket
@@ -156,17 +157,24 @@
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r)
+ sig.RSASignature = new(encoding.MPI)
+ _, err = sig.RSASignature.ReadFrom(r)
case PubKeyAlgoDSA:
- sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r)
- if err == nil {
- sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
+ sig.DSASigR = new(encoding.MPI)
+ if _, err = sig.DSASigR.ReadFrom(r); err != nil {
+ return
}
+
+ sig.DSASigS = new(encoding.MPI)
+ _, err = sig.DSASigS.ReadFrom(r)
case PubKeyAlgoECDSA:
- sig.ECDSASigR.bytes, sig.ECDSASigR.bitLength, err = readMPI(r)
- if err == nil {
- sig.ECDSASigS.bytes, sig.ECDSASigS.bitLength, err = readMPI(r)
+ sig.ECDSASigR = new(encoding.MPI)
+ if _, err = sig.ECDSASigR.ReadFrom(r); err != nil {
+ return
}
+
+ sig.ECDSASigS = new(encoding.MPI)
+ _, err = sig.ECDSASigS.ReadFrom(r)
default:
panic("unreachable")
}
@@ -518,8 +526,10 @@
switch priv.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
// supports both *rsa.PrivateKey and crypto.Signer
- sig.RSASignature.bytes, err = priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
- sig.RSASignature.bitLength = uint16(8 * len(sig.RSASignature.bytes))
+ sigdata, err := priv.PrivateKey.(crypto.Signer).Sign(config.Random(), digest, sig.Hash)
+ if err == nil {
+ sig.RSASignature = encoding.NewMPI(sigdata)
+ }
case PubKeyAlgoDSA:
dsaPriv := priv.PrivateKey.(*dsa.PrivateKey)
@@ -530,10 +540,8 @@
}
r, s, err := dsa.Sign(config.Random(), dsaPriv, digest)
if err == nil {
- sig.DSASigR.bytes = r.Bytes()
- sig.DSASigR.bitLength = uint16(8 * len(sig.DSASigR.bytes))
- sig.DSASigS.bytes = s.Bytes()
- sig.DSASigS.bitLength = uint16(8 * len(sig.DSASigS.bytes))
+ sig.DSASigR = new(encoding.MPI).SetBig(r)
+ sig.DSASigS = new(encoding.MPI).SetBig(s)
}
case PubKeyAlgoECDSA:
var r, s *big.Int
@@ -548,8 +556,8 @@
}
}
if err == nil {
- sig.ECDSASigR = fromBig(r)
- sig.ECDSASigS = fromBig(s)
+ sig.ECDSASigR = new(encoding.MPI).SetBig(r)
+ sig.ECDSASigS = new(encoding.MPI).SetBig(s)
}
default:
err = errors.UnsupportedError("public key algorithm: " + strconv.Itoa(int(sig.PubKeyAlgo)))
@@ -600,20 +608,20 @@
if len(sig.outSubpackets) == 0 {
sig.outSubpackets = sig.rawSubpackets
}
- if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil && sig.ECDSASigR.bytes == nil {
+ if sig.RSASignature == nil && sig.DSASigR == nil && sig.ECDSASigR == nil {
return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
}
sigLength := 0
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- sigLength = 2 + len(sig.RSASignature.bytes)
+ sigLength = int(sig.RSASignature.EncodedLength())
case PubKeyAlgoDSA:
- sigLength = 2 + len(sig.DSASigR.bytes)
- sigLength += 2 + len(sig.DSASigS.bytes)
+ sigLength = int(sig.DSASigR.EncodedLength())
+ sigLength += int(sig.DSASigS.EncodedLength())
case PubKeyAlgoECDSA:
- sigLength = 2 + len(sig.ECDSASigR.bytes)
- sigLength += 2 + len(sig.ECDSASigS.bytes)
+ sigLength = int(sig.ECDSASigR.EncodedLength())
+ sigLength += int(sig.ECDSASigS.EncodedLength())
default:
panic("impossible")
}
@@ -648,11 +656,17 @@
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- err = writeMPIs(w, sig.RSASignature)
+ _, err = w.Write(sig.RSASignature.EncodedBytes())
case PubKeyAlgoDSA:
- err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
+ if _, err = w.Write(sig.DSASigR.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(sig.DSASigS.EncodedBytes())
case PubKeyAlgoECDSA:
- err = writeMPIs(w, sig.ECDSASigR, sig.ECDSASigS)
+ if _, err = w.Write(sig.ECDSASigR.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(sig.ECDSASigS.EncodedBytes())
default:
panic("impossible")
}
diff --git a/openpgp/packet/signature_v3.go b/openpgp/packet/signature_v3.go
index 6edff88..c6e8d72 100644
--- a/openpgp/packet/signature_v3.go
+++ b/openpgp/packet/signature_v3.go
@@ -13,6 +13,7 @@
"time"
"golang.org/x/crypto/openpgp/errors"
+ "golang.org/x/crypto/openpgp/internal/encoding"
"golang.org/x/crypto/openpgp/s2k"
)
@@ -28,8 +29,8 @@
Hash crypto.Hash
HashTag [2]byte
- RSASignature parsedMPI
- DSASigR, DSASigS parsedMPI
+ RSASignature encoding.Field
+ DSASigR, DSASigS encoding.Field
}
func (sig *SignatureV3) parse(r io.Reader) (err error) {
@@ -88,12 +89,16 @@
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- sig.RSASignature.bytes, sig.RSASignature.bitLength, err = readMPI(r)
+ sig.RSASignature = new(encoding.MPI)
+ _, err = sig.RSASignature.ReadFrom(r)
case PubKeyAlgoDSA:
- if sig.DSASigR.bytes, sig.DSASigR.bitLength, err = readMPI(r); err != nil {
+ sig.DSASigR = new(encoding.MPI)
+ if _, err = sig.DSASigR.ReadFrom(r); err != nil {
return
}
- sig.DSASigS.bytes, sig.DSASigS.bitLength, err = readMPI(r)
+
+ sig.DSASigS = new(encoding.MPI)
+ _, err = sig.DSASigS.ReadFrom(r)
default:
panic("unreachable")
}
@@ -130,15 +135,18 @@
return
}
- if sig.RSASignature.bytes == nil && sig.DSASigR.bytes == nil {
+ if sig.RSASignature.Bytes() == nil && sig.DSASigR.Bytes() == nil {
return errors.InvalidArgumentError("Signature: need to call Sign, SignUserId or SignKey before Serialize")
}
switch sig.PubKeyAlgo {
case PubKeyAlgoRSA, PubKeyAlgoRSASignOnly:
- err = writeMPIs(w, sig.RSASignature)
+ _, err = w.Write(sig.RSASignature.EncodedBytes())
case PubKeyAlgoDSA:
- err = writeMPIs(w, sig.DSASigR, sig.DSASigS)
+ if _, err = w.Write(sig.DSASigR.EncodedBytes()); err != nil {
+ return
+ }
+ _, err = w.Write(sig.DSASigS.EncodedBytes())
default:
panic("impossible")
}