passforios-gopenpgp/crypto/key.go
wussler e65ed17b41
Refactor api (#6)
* Refactor library, remove duplicates

* Rebuild structure to use Messages and Signature models

* Use PGPSplitMessage

* Remove signature model

* Various fixes

* Add helpers with tests

* Fixes, add some docs, add tests

* Add attachment helpers

* Add helpers Symmetric encryption

* Edit docs + examples

* Rename kr to keyRing

* Various fixes for documentation

* Edit JSON handling functions, add decrypt keyring via token

* Add proposal changes doc

* Fix CI

* Drop *Message functions, join CleartextMessage and BinaryMessage

* Change canonicalization and trimming only to text signatures

* Add cleartextsignature, detach signature from message model, move helpers

* Documentation, remove optional parameters

* Move verification to separate model

* Don't return message in VerifyDetached

* Update table of contents in readme

* Appease golint

* Run go fmt

* Rename Encrypt/DecryptMessageWithPassword to ..WithToken

These functions shouldn't be used with user-provided passwords,
as they don't do any key-stretching.

* Change key generation usernames
2019-06-03 17:00:01 +02:00

234 lines
6.5 KiB
Go

package crypto
import (
"bytes"
"crypto"
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"
"time"
"github.com/ProtonMail/gopenpgp/armor"
"github.com/ProtonMail/gopenpgp/constants"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/packet"
)
// IsKeyExpired checks whether the given (unarmored, binary) key is expired.
func (pgp *GopenPGP) IsKeyExpired(publicKey []byte) (bool, error) {
now := pgp.getNow()
pubKeyReader := bytes.NewReader(publicKey)
pubKeyEntries, err := openpgp.ReadKeyRing(pubKeyReader)
if err != nil {
return true, err
}
candidateSubkey := -1
for _, e := range pubKeyEntries {
var maxTime time.Time
for i, subkey := range e.Subkeys {
if subkey.Sig.FlagsValid &&
subkey.Sig.FlagEncryptCommunications &&
subkey.PublicKey.PubKeyAlgo.CanEncrypt() &&
!subkey.PublicKey.KeyExpired(subkey.Sig, now) &&
(maxTime.IsZero() || subkey.Sig.CreationTime.After(maxTime)) {
candidateSubkey = i
maxTime = subkey.Sig.CreationTime
}
}
if candidateSubkey != -1 {
return false, nil
}
// If we don't have any candidate subkeys for encryption and
// the primary key doesn't have any usage metadata then we
// assume that the primary key is ok. Or, if the primary key is
// marked as ok to encrypt to, then we can obviously use it.
var firstIdentity *openpgp.Identity
for _, ident := range e.Identities {
if firstIdentity == nil {
firstIdentity = ident
}
if ident.SelfSignature.IsPrimaryId != nil && *ident.SelfSignature.IsPrimaryId {
firstIdentity = ident
break
}
}
if firstIdentity != nil {
i := firstIdentity
if !i.SelfSignature.FlagsValid || i.SelfSignature.FlagEncryptCommunications &&
e.PrimaryKey.PubKeyAlgo.CanEncrypt() &&
!e.PrimaryKey.KeyExpired(i.SelfSignature, now) {
return false, nil
}
}
}
return true, errors.New("keys expired")
}
// IsArmoredKeyExpired checks whether the given armored key is expired.
func (pgp *GopenPGP) IsArmoredKeyExpired(publicKey string) (bool, error) {
rawPubKey, err := armor.Unarmor(publicKey)
if err != nil {
return false, err
}
return pgp.IsKeyExpired(rawPubKey)
}
func (pgp *GopenPGP) generateKey(
name, email, passphrase, keyType string,
bits int,
prime1, prime2, prime3, prime4 []byte,
) (string, error) {
if len(email) <= 0 {
return "", errors.New("invalid email format")
}
if len(name) <= 0 {
return "", errors.New("invalid name format")
}
comments := ""
cfg := &packet.Config{
Algorithm: packet.PubKeyAlgoRSA,
RSABits: bits,
Time: pgp.getTimeGenerator(),
DefaultHash: crypto.SHA256,
DefaultCipher: packet.CipherAES256,
}
if keyType == "x25519" {
cfg.Algorithm = packet.PubKeyAlgoEdDSA
}
if prime1 != nil && prime2 != nil && prime3 != nil && prime4 != nil {
var bigPrimes [4]*big.Int
bigPrimes[0] = new(big.Int)
bigPrimes[0].SetBytes(prime1)
bigPrimes[1] = new(big.Int)
bigPrimes[1].SetBytes(prime2)
bigPrimes[2] = new(big.Int)
bigPrimes[2].SetBytes(prime3)
bigPrimes[3] = new(big.Int)
bigPrimes[3].SetBytes(prime4)
cfg.RSAPrimes = bigPrimes[:]
}
newEntity, err := openpgp.NewEntity(name, comments, email, cfg)
if err != nil {
return "", err
}
if err := newEntity.SelfSign(nil); err != nil {
return "", err
}
rawPwd := []byte(passphrase)
if newEntity.PrivateKey != nil && !newEntity.PrivateKey.Encrypted {
if err := newEntity.PrivateKey.Encrypt(rawPwd); err != nil {
return "", err
}
}
for _, sub := range newEntity.Subkeys {
if sub.PrivateKey != nil && !sub.PrivateKey.Encrypted {
if err := sub.PrivateKey.Encrypt(rawPwd); err != nil {
return "", err
}
}
}
w := bytes.NewBuffer(nil)
if err := newEntity.SerializePrivateNoSign(w, nil); err != nil {
return "", err
}
serialized := w.Bytes()
return armor.ArmorWithType(serialized, constants.PrivateKeyHeader)
}
// GenerateRSAKeyWithPrimes generates a RSA key using the given primes.
func (pgp *GopenPGP) GenerateRSAKeyWithPrimes(
name, email, passphrase string,
bits int,
primeone, primetwo, primethree, primefour []byte,
) (string, error) {
return pgp.generateKey(name, email, passphrase, "rsa", bits, primeone, primetwo, primethree, primefour)
}
// GenerateKey generates a key of the given keyType ("rsa" or "x25519").
// If keyType is "rsa", bits is the RSA bitsize of the key.
// If keyType is "x25519" bits is unused.
func (pgp *GopenPGP) GenerateKey(name, email, passphrase, keyType string, bits int) (string, error) {
return pgp.generateKey(name, email, passphrase, keyType, bits, nil, nil, nil, nil)
}
// UpdatePrivateKeyPassphrase decrypts the given armored privateKey with oldPassphrase,
// re-encrypts it with newPassphrase, and returns the new armored key.
func (pgp *GopenPGP) UpdatePrivateKeyPassphrase(
privateKey string, oldPassphrase string, newPassphrase string,
) (string, error) {
privKey := strings.NewReader(privateKey)
privKeyEntries, err := openpgp.ReadArmoredKeyRing(privKey)
if err != nil {
return "", err
}
oldrawPwd := []byte(oldPassphrase)
newRawPwd := []byte(newPassphrase)
w := bytes.NewBuffer(nil)
for _, e := range privKeyEntries {
if e.PrivateKey != nil && e.PrivateKey.Encrypted {
if err := e.PrivateKey.Decrypt(oldrawPwd); err != nil {
return "", err
}
}
if e.PrivateKey != nil && !e.PrivateKey.Encrypted {
if err := e.PrivateKey.Encrypt(newRawPwd); err != nil {
return "", err
}
}
for _, sub := range e.Subkeys {
if sub.PrivateKey != nil && sub.PrivateKey.Encrypted {
if err := sub.PrivateKey.Decrypt(oldrawPwd); err != nil {
return "", err
}
}
if sub.PrivateKey != nil && !sub.PrivateKey.Encrypted {
if err := sub.PrivateKey.Encrypt(newRawPwd); err != nil {
return "", err
}
}
}
if err := e.SerializePrivateNoSign(w, nil); err != nil {
return "", err
}
}
serialized := w.Bytes()
return armor.ArmorWithType(serialized, constants.PrivateKeyHeader)
}
// PrintFingerprints is a debug helper function that prints the key and subkey fingerprints.
func (pgp *GopenPGP) PrintFingerprints(pubKey string) (string, error) {
pubKeyReader := strings.NewReader(pubKey)
entries, err := openpgp.ReadArmoredKeyRing(pubKeyReader)
if err != nil {
return "", err
}
for _, e := range entries {
for _, subKey := range e.Subkeys {
if !subKey.Sig.FlagsValid || subKey.Sig.FlagEncryptStorage || subKey.Sig.FlagEncryptCommunications {
fmt.Println("SubKey:" + hex.EncodeToString(subKey.PublicKey.Fingerprint[:]))
}
}
fmt.Println("PrimaryKey:" + hex.EncodeToString(e.PrimaryKey.Fingerprint[:]))
}
return "", nil
}