Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[crypto/openpgp] Serialize encrypted PrivateKey #17

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions openpgp/packet/packet.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/openpgp/errors"
"io"
"math/big"

"golang.org/x/crypto/cast5"
"golang.org/x/crypto/openpgp/errors"
)

// readFull is the same as io.ReadFull except that reading zero bytes returns
Expand Down
216 changes: 189 additions & 27 deletions openpgp/packet/private_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ package packet

import (
"bytes"
"crypto"
"crypto/cipher"
"crypto/dsa"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"encoding/binary"
"io"
"io/ioutil"
"math/big"
Expand All @@ -33,8 +36,22 @@ type PrivateKey struct {
PrivateKey interface{} // An *rsa.PrivateKey or *dsa.PrivateKey.
sha1Checksum bool
iv []byte

// s2k related
salt []byte
s2kMode uint8
s2kConfig s2k.Config
s2kType S2KType
}

type S2KType uint8

const (
S2KNON S2KType = 0
S2KSHA1 S2KType = 254
S2KCHECKSUM S2KType = 255
)

func NewRSAPrivateKey(currentTime time.Time, priv *rsa.PrivateKey) *PrivateKey {
pk := new(PrivateKey)
pk.PublicKey = *NewRSAPublicKey(currentTime, &priv.PublicKey)
Expand Down Expand Up @@ -74,24 +91,24 @@ func (pk *PrivateKey) parse(r io.Reader) (err error) {
return
}

s2kType := buf[0]
pk.s2kType = S2KType(buf[0])

switch s2kType {
case 0:
switch pk.s2kType {
case S2KNON:
pk.s2k = nil
pk.Encrypted = false
case 254, 255:
case S2KSHA1, S2KCHECKSUM:
_, err = readFull(r, buf[:])
if err != nil {
return
}
pk.cipher = CipherFunction(buf[0])
pk.Encrypted = true
pk.s2k, err = s2k.Parse(r)
pk.s2k, pk.s2kMode, pk.s2kConfig.Hash, pk.salt, pk.s2kConfig.S2KCount, err = s2k.Parse2(r)
if err != nil {
return
}
if s2kType == 254 {
if pk.s2kType == S2KSHA1 {
pk.sha1Checksum = true
}
default:
Expand Down Expand Up @@ -130,40 +147,86 @@ func mod64kHash(d []byte) uint16 {
return h
}

func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
// TODO(agl): support encrypted private keys
buf := bytes.NewBuffer(nil)
err = pk.PublicKey.serializeWithoutHeaders(buf)
if err != nil {
return
func (pk *PrivateKey) SerializeEncrypted(w io.Writer) error {
privateKeyBuf := bytes.NewBuffer(nil)
encodedKeyBuf := bytes.NewBuffer(nil)
encodedKeyBuf.Write([]byte{uint8(pk.s2kType)})
encodedKeyBuf.Write([]byte{uint8(pk.cipher)})
encodedKeyBuf.Write([]byte{pk.s2kMode})
hashID, ok := s2k.HashToHashId(pk.s2kConfig.Hash)
if !ok {
return errors.UnsupportedError("no such hash")
}
buf.WriteByte(0 /* no encryption */)
encodedKeyBuf.Write([]byte{hashID})
encodedKeyBuf.Write(pk.salt)
encodedKeyBuf.Write([]byte{pk.s2kConfig.EncodedCount()})

privateKeyBuf := bytes.NewBuffer(nil)
privateKeyBuf.Write(pk.encryptedData)

encodedKey := encodedKeyBuf.Bytes()
privateKeyBytes := privateKeyBuf.Bytes()

w.Write(encodedKey)
w.Write(pk.iv)
w.Write(privateKeyBytes)

return nil
}

func (pk *PrivateKey) SerializeUnEncrypted(w io.Writer) (err error) {
buf := bytes.NewBuffer(nil)
buf.Write([]byte{uint8(S2KNON)} /* no encryption */)
switch priv := pk.PrivateKey.(type) {
case *rsa.PrivateKey:
err = serializeRSAPrivateKey(privateKeyBuf, priv)
err = serializeRSAPrivateKey(buf, priv)
case *dsa.PrivateKey:
err = serializeDSAPrivateKey(privateKeyBuf, priv)
err = serializeDSAPrivateKey(buf, priv)
case *elgamal.PrivateKey:
err = serializeElGamalPrivateKey(privateKeyBuf, priv)
err = serializeElGamalPrivateKey(buf, priv)
case *ecdsa.PrivateKey:
err = serializeECDSAPrivateKey(privateKeyBuf, priv)
err = serializeECDSAPrivateKey(buf, priv)
default:
err = errors.InvalidArgumentError("unknown private key type")
}
privateKeyBytes := buf.Bytes()
if pk.sha1Checksum {
h := sha1.New()
h.Write(privateKeyBytes)
sum := h.Sum(nil)
privateKeyBytes = append(privateKeyBytes, sum...)
} else {
checksum := mod64kHash(privateKeyBytes)
var checksumBytes [2]byte
checksumBytes[0] = byte(checksum >> 8)
checksumBytes[1] = byte(checksum)
privateKeyBytes = append(privateKeyBytes, checksumBytes[:]...)
}
w.Write(privateKeyBytes)
return
}

func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
// TODO(agl): support encrypted private keys
buf := bytes.NewBuffer(nil)
err = pk.PublicKey.serializeWithoutHeaders(buf)
if err != nil {
return
}

privateKeyBuf := bytes.NewBuffer(nil)
if pk.Encrypted {
pk.SerializeEncrypted(privateKeyBuf)
} else {
pk.SerializeUnEncrypted(privateKeyBuf)
}

ptype := packetTypePrivateKey
contents := buf.Bytes()
privateKeyBytes := privateKeyBuf.Bytes()
if pk.IsSubkey {
ptype = packetTypePrivateSubkey
}
err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes)+2)
err = serializeHeader(w, ptype, len(contents)+len(privateKeyBytes))
if err != nil {
return
}
Expand All @@ -175,13 +238,6 @@ func (pk *PrivateKey) Serialize(w io.Writer) (err error) {
if err != nil {
return
}

checksum := mod64kHash(privateKeyBytes)
var checksumBytes [2]byte
checksumBytes[0] = byte(checksum >> 8)
checksumBytes[1] = byte(checksum)
_, err = w.Write(checksumBytes[:])

return
}

Expand Down Expand Up @@ -213,6 +269,112 @@ func serializeECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
return writeBig(w, priv.D)
}

func (pk *PrivateKey) Encrypt(passphrase []byte) error {
privateKeyBuf := bytes.NewBuffer(nil)
err := pk.SerializePGPPrivate(privateKeyBuf)
if err != nil {
return err
}

//Default config of private key encryption
pk.cipher = CipherAES128
pk.s2kMode = 3 //Iterated
pk.s2kConfig = s2k.Config{
S2KCount: 65536,
Hash: crypto.SHA1,
}

privateKeyBytes := privateKeyBuf.Bytes()
key := make([]byte, pk.cipher.KeySize())
pk.salt = make([]byte, 8)
rand.Read(pk.salt)

pk.s2k = func(out, in []byte) {
s2k.Iterated(out, pk.s2kConfig.Hash.New(), in, pk.salt, pk.s2kConfig.S2KCount)
}
pk.s2k(key, passphrase)
block := pk.cipher.new(key)
pk.iv = make([]byte, pk.cipher.blockSize())
rand.Read(pk.iv)
cfb := cipher.NewCFBEncrypter(block, pk.iv)

if pk.sha1Checksum {
pk.s2kType = S2KSHA1
h := sha1.New()
h.Write(privateKeyBytes)
sum := h.Sum(nil)
privateKeyBytes = append(privateKeyBytes, sum...)
} else {
pk.s2kType = S2KCHECKSUM
var sum uint16
for i := 0; i < len(privateKeyBytes); i++ {
sum += uint16(privateKeyBytes[i])
}
privateKeyBytes = append(privateKeyBytes, uint8(sum>>8))
privateKeyBytes = append(privateKeyBytes, uint8(sum))
}

pk.encryptedData = make([]byte, len(privateKeyBytes))

cfb.XORKeyStream(pk.encryptedData, privateKeyBytes)

pk.Encrypted = true
return err
}

func (pk *PrivateKey) SerializePGPPrivate(privateKeyBuf io.Writer) error {
var err error
switch priv := pk.PrivateKey.(type) {
case *rsa.PrivateKey:
err = serializePGPRSAPrivateKey(privateKeyBuf, priv)
case *dsa.PrivateKey:
err = serializePGPDSAPrivateKey(privateKeyBuf, priv)
case *elgamal.PrivateKey:
err = serializePGPElGamalPrivateKey(privateKeyBuf, priv)
case *ecdsa.PrivateKey:
err = serializePGPECDSAPrivateKey(privateKeyBuf, priv)
default:
err = errors.InvalidArgumentError("unknown private key type")
}
return err
}

func serializePGPRSAPrivateKey(w io.Writer, priv *rsa.PrivateKey) error {
binary.Write(w, binary.BigEndian, priv.D.BitLen())
err := writeBig(w, priv.D)
if err != nil {
return err
}
binary.Write(w, binary.BigEndian, priv.Primes[0].BitLen())
err = writeBig(w, priv.Primes[0])
if err != nil {
return err
}
binary.Write(w, binary.BigEndian, priv.Primes[1].BitLen())
err = writeBig(w, priv.Primes[1])
if err != nil {
return err
}
u := new(big.Int).ModInverse(priv.Primes[0], priv.Primes[1])
binary.Write(w, binary.BigEndian, u.BitLen())
return writeBig(w, u)
}

func serializePGPDSAPrivateKey(w io.Writer, priv *dsa.PrivateKey) error {
binary.Write(w, binary.BigEndian, priv.X.BitLen())
return writeBig(w, priv.X)
}

func serializePGPElGamalPrivateKey(w io.Writer, priv *elgamal.PrivateKey) error {
binary.Write(w, binary.BigEndian, priv.X.BitLen())
return writeBig(w, priv.X)
}

func serializePGPECDSAPrivateKey(w io.Writer, priv *ecdsa.PrivateKey) error {
binary.Write(w, binary.BigEndian, priv.D.BitLen())
return writeBig(w, priv.D)
}

// Decrypt decrypts an encrypted private key using a passphrase.
func (pk *PrivateKey) Decrypt(passphrase []byte) error {
if !pk.Encrypted {
Expand All @@ -223,7 +385,6 @@ func (pk *PrivateKey) Decrypt(passphrase []byte) error {
pk.s2k(key, passphrase)
block := pk.cipher.new(key)
cfb := cipher.NewCFBDecrypter(block, pk.iv)

data := make([]byte, len(pk.encryptedData))
cfb.XORKeyStream(data, pk.encryptedData)

Expand Down Expand Up @@ -297,6 +458,7 @@ func (pk *PrivateKey) parseRSAPrivateKey(data []byte) (err error) {
return err
}
rsaPriv.Precompute()

pk.PrivateKey = rsaPriv
pk.Encrypted = false
pk.encryptedData = nil
Expand Down
Loading