diff --git a/pkg/acl/testutils/threadbuilder/keychain.go b/pkg/acl/testutils/threadbuilder/keychain.go index c8532a41..1596e7d2 100644 --- a/pkg/acl/testutils/threadbuilder/keychain.go +++ b/pkg/acl/testutils/threadbuilder/keychain.go @@ -21,7 +21,7 @@ type Keychain struct { ReadKeys map[string]*SymKey ReadKeysByHash map[uint64]*SymKey GeneratedIdentities map[string]string - coder *keys.Ed25519SigningPubKeyDecoder + coder keys.SigningPubKeyDecoder } func NewKeychain() *Keychain { diff --git a/util/keys/ed25519.go b/util/keys/ed25519.go new file mode 100644 index 00000000..3e955151 --- /dev/null +++ b/util/keys/ed25519.go @@ -0,0 +1,175 @@ +package keys + +import ( + "bytes" + "crypto/ed25519" + "crypto/rand" + "crypto/subtle" + "errors" + "fmt" + "io" + + "github.com/anytypeio/go-anytype-infrastructure-experiments/util/strkey" +) + +// Ed25519PrivateKey is an ed25519 private key. +type Ed25519PrivateKey struct { + k ed25519.PrivateKey +} + +// Ed25519PublicKey is an ed25519 public key. +type Ed25519PublicKey struct { + k ed25519.PublicKey +} + +func NewSigningEd25519PubKeyFromBytes(bytes []byte) (SigningPubKey, error) { + return UnmarshalEd25519PublicKey(bytes) +} + +func GenerateRandomEd25519KeyPair() (SigningPrivKey, SigningPubKey, error) { + return GenerateEd25519Key(rand.Reader) +} + +// GenerateEd25519Key generates a new ed25519 private and public key pair. +func GenerateEd25519Key(src io.Reader) (SigningPrivKey, SigningPubKey, error) { + pub, priv, err := ed25519.GenerateKey(src) + if err != nil { + return nil, nil, err + } + + return &Ed25519PrivateKey{ + k: priv, + }, + &Ed25519PublicKey{ + k: pub, + }, + nil +} + +// Raw private key bytes. +func (k *Ed25519PrivateKey) Raw() ([]byte, error) { + // The Ed25519 private key contains two 32-bytes curve points, the private + // key and the public key. + // It makes it more efficient to get the public key without re-computing an + // elliptic curve multiplication. + buf := make([]byte, len(k.k)) + copy(buf, k.k) + + return buf, nil +} + +func (k *Ed25519PrivateKey) pubKeyBytes() []byte { + return k.k[ed25519.PrivateKeySize-ed25519.PublicKeySize:] +} + +// Equals compares two ed25519 private keys. +func (k *Ed25519PrivateKey) Equals(o Key) bool { + edk, ok := o.(*Ed25519PrivateKey) + if !ok { + return keyEquals(k, o) + } + + return subtle.ConstantTimeCompare(k.k, edk.k) == 1 +} + +// GetPublic returns an ed25519 public key from a private key. +func (k *Ed25519PrivateKey) GetPublic() SigningPubKey { + return &Ed25519PublicKey{k: k.pubKeyBytes()} +} + +// Sign returns a signature from an input message. +func (k *Ed25519PrivateKey) Sign(msg []byte) ([]byte, error) { + return ed25519.Sign(k.k, msg), nil +} + +// Raw public key bytes. +func (k *Ed25519PublicKey) Raw() ([]byte, error) { + return k.k, nil +} + +// Equals compares two ed25519 public keys. +func (k *Ed25519PublicKey) Equals(o Key) bool { + edk, ok := o.(*Ed25519PublicKey) + if !ok { + return keyEquals(k, o) + } + + return bytes.Equal(k.k, edk.k) +} + +// Verify checks a signature agains the input data. +func (k *Ed25519PublicKey) Verify(data []byte, sig []byte) (bool, error) { + return ed25519.Verify(k.k, data, sig), nil +} + +// UnmarshalEd25519PublicKey returns a public key from input bytes. +func UnmarshalEd25519PublicKey(data []byte) (SigningPubKey, error) { + if len(data) != 32 { + return nil, errors.New("expect ed25519 public key data size to be 32") + } + + return &Ed25519PublicKey{ + k: ed25519.PublicKey(data), + }, nil +} + +// UnmarshalEd25519PrivateKey returns a private key from input bytes. +func UnmarshalEd25519PrivateKey(data []byte) (SigningPrivKey, error) { + switch len(data) { + case ed25519.PrivateKeySize + ed25519.PublicKeySize: + // Remove the redundant public key. See issue #36. + redundantPk := data[ed25519.PrivateKeySize:] + pk := data[ed25519.PrivateKeySize-ed25519.PublicKeySize : ed25519.PrivateKeySize] + if subtle.ConstantTimeCompare(pk, redundantPk) == 0 { + return nil, errors.New("expected redundant ed25519 public key to be redundant") + } + + // No point in storing the extra data. + newKey := make([]byte, ed25519.PrivateKeySize) + copy(newKey, data[:ed25519.PrivateKeySize]) + data = newKey + case ed25519.PrivateKeySize: + default: + return nil, fmt.Errorf( + "expected ed25519 data size to be %d or %d, got %d", + ed25519.PrivateKeySize, + ed25519.PrivateKeySize+ed25519.PublicKeySize, + len(data), + ) + } + + return &Ed25519PrivateKey{ + k: ed25519.PrivateKey(data), + }, nil +} + +type Ed25519SigningPubKeyDecoder struct{} + +func NewEd25519Decoder() SigningPubKeyDecoder { + return &Ed25519SigningPubKeyDecoder{} +} + +func (e *Ed25519SigningPubKeyDecoder) DecodeFromBytes(bytes []byte) (SigningPubKey, error) { + return NewSigningEd25519PubKeyFromBytes(bytes) +} + +func (e *Ed25519SigningPubKeyDecoder) DecodeFromString(identity string) (SigningPubKey, error) { + pubKeyRaw, err := strkey.Decode(0x5b, identity) + if err != nil { + return nil, err + } + + return e.DecodeFromBytes(pubKeyRaw) +} + +func (e *Ed25519SigningPubKeyDecoder) DecodeFromStringIntoBytes(identity string) ([]byte, error) { + return strkey.Decode(0x5b, identity) +} + +func (e *Ed25519SigningPubKeyDecoder) EncodeToString(pubkey SigningPubKey) (string, error) { + raw, err := pubkey.Raw() + if err != nil { + return "", err + } + return strkey.Encode(0x5b, raw) +} diff --git a/util/keys/encryptionkey.go b/util/keys/encryptionkey.go new file mode 100644 index 00000000..9b6f869f --- /dev/null +++ b/util/keys/encryptionkey.go @@ -0,0 +1,14 @@ +package keys + +type EncryptionPrivKey interface { + Key + + Decrypt([]byte) ([]byte, error) + GetPublic() EncryptionPubKey +} + +type EncryptionPubKey interface { + Key + + Encrypt(data []byte) ([]byte, error) +} diff --git a/util/keys/key.go b/util/keys/key.go new file mode 100644 index 00000000..b9b00514 --- /dev/null +++ b/util/keys/key.go @@ -0,0 +1,7 @@ +package keys + +type Key interface { + Equals(Key) bool + + Raw() ([]byte, error) +} diff --git a/util/keys/keys.go b/util/keys/keys.go deleted file mode 100644 index 796b6ab3..00000000 --- a/util/keys/keys.go +++ /dev/null @@ -1,256 +0,0 @@ -package keys - -import ( - "crypto/rand" - "crypto/rsa" - "crypto/sha512" - "crypto/subtle" - "crypto/x509" - "errors" - "io" - - "github.com/anytypeio/go-anytype-infrastructure-experiments/util/strkey" - "github.com/libp2p/go-libp2p-core/crypto" - crypto_pb "github.com/libp2p/go-libp2p-core/crypto/pb" -) - -type SigningPubKey crypto.PubKey -type SigningPrivKey crypto.PrivKey - -var MinRsaKeyBits = 2048 - -var ErrKeyLengthTooSmall = errors.New("error key length too small") - -type Key interface { - Equals(Key) bool - - Raw() ([]byte, error) -} - -type EncryptionPrivKey interface { - Key - - Decrypt([]byte) ([]byte, error) - GetPublic() EncryptionPubKey -} - -type EncryptionPubKey interface { - Key - - Encrypt(data []byte) ([]byte, error) -} - -type EncryptionRsaPrivKey struct { - privKey rsa.PrivateKey -} - -type EncryptionRsaPubKey struct { - pubKey rsa.PublicKey -} - -func (e *EncryptionRsaPubKey) Equals(key Key) bool { - other, ok := (key).(*EncryptionRsaPubKey) - if !ok { - return keyEquals(e, key) - } - - return e.pubKey.N.Cmp(other.pubKey.N) == 0 && e.pubKey.E == other.pubKey.E -} - -func (e *EncryptionRsaPubKey) Raw() ([]byte, error) { - return x509.MarshalPKIXPublicKey(&e.pubKey) -} - -func (e *EncryptionRsaPubKey) Encrypt(data []byte) ([]byte, error) { - hash := sha512.New() - return rsa.EncryptOAEP(hash, rand.Reader, &e.pubKey, data, nil) -} - -func (e *EncryptionRsaPrivKey) Equals(key Key) bool { - other, ok := (key).(*EncryptionRsaPrivKey) - if !ok { - return keyEquals(e, key) - } - - return e.privKey.N.Cmp(other.privKey.N) == 0 && e.privKey.E == other.privKey.E -} - -func (e *EncryptionRsaPrivKey) Raw() ([]byte, error) { - b := x509.MarshalPKCS1PrivateKey(&e.privKey) - return b, nil -} - -func (e *EncryptionRsaPrivKey) Decrypt(bytes []byte) ([]byte, error) { - hash := sha512.New() - return rsa.DecryptOAEP(hash, rand.Reader, &e.privKey, bytes, nil) -} - -func (e *EncryptionRsaPrivKey) GetPublic() EncryptionPubKey { - return &EncryptionRsaPubKey{pubKey: e.privKey.PublicKey} -} - -func GenerateRandomRSAKeyPair(bits int) (EncryptionPrivKey, EncryptionPubKey, error) { - return GenerateRSAKeyPair(bits, rand.Reader) -} - -func GenerateRSAKeyPair(bits int, src io.Reader) (EncryptionPrivKey, EncryptionPubKey, error) { - if bits < MinRsaKeyBits { - return nil, nil, ErrKeyLengthTooSmall - } - priv, err := rsa.GenerateKey(src, bits) - if err != nil { - return nil, nil, err - } - pk := priv.PublicKey - return &EncryptionRsaPrivKey{privKey: *priv}, &EncryptionRsaPubKey{pubKey: pk}, nil -} - -func NewEncryptionRsaPrivKeyFromBytes(bytes []byte) (EncryptionPrivKey, error) { - sk, err := x509.ParsePKCS1PrivateKey(bytes) - if err != nil { - return nil, err - } - if sk.N.BitLen() < MinRsaKeyBits { - return nil, ErrKeyLengthTooSmall - } - return &EncryptionRsaPrivKey{privKey: *sk}, nil -} - -func NewEncryptionRsaPubKeyFromBytes(bytes []byte) (EncryptionPubKey, error) { - pub, err := x509.ParsePKIXPublicKey(bytes) - if err != nil { - return nil, err - } - pk, ok := pub.(*rsa.PublicKey) - if !ok { - return nil, errors.New("not actually an rsa public key") - } - if pk.N.BitLen() < MinRsaKeyBits { - return nil, ErrKeyLengthTooSmall - } - - return &EncryptionRsaPubKey{pubKey: *pk}, nil -} - -func NewSigningEd25519PubKeyFromBytes(bytes []byte) (SigningPubKey, error) { - return crypto.UnmarshalEd25519PublicKey(bytes) -} - -func GenerateRandomEd25519KeyPair() (SigningPrivKey, SigningPubKey, error) { - return crypto.GenerateEd25519Key(rand.Reader) -} - -func keyEquals(k1, k2 Key) bool { - a, err := k1.Raw() - if err != nil { - return false - } - b, err := k2.Raw() - if err != nil { - return false - } - return subtle.ConstantTimeCompare(a, b) == 1 -} - -type Ed25519SigningPubKeyDecoder struct{} - -func NewEd25519Decoder() *Ed25519SigningPubKeyDecoder { - return &Ed25519SigningPubKeyDecoder{} -} - -func (e *Ed25519SigningPubKeyDecoder) DecodeFromBytes(bytes []byte) (SigningPubKey, error) { - return NewSigningEd25519PubKeyFromBytes(bytes) -} - -func (e *Ed25519SigningPubKeyDecoder) DecodeFromString(identity string) (SigningPubKey, error) { - pubKeyRaw, err := strkey.Decode(0x5b, identity) - if err != nil { - return nil, err - } - - return e.DecodeFromBytes(pubKeyRaw) -} - -func (e *Ed25519SigningPubKeyDecoder) DecodeFromStringIntoBytes(identity string) ([]byte, error) { - return strkey.Decode(0x5b, identity) -} - -func (e *Ed25519SigningPubKeyDecoder) EncodeToString(pubkey crypto.PubKey) (string, error) { - raw, err := pubkey.Raw() - if err != nil { - return "", err - } - return strkey.Encode(0x5b, raw) -} - -type SigningPubKeyDecoder interface { - DecodeFromBytes(bytes []byte) (SigningPubKey, error) - DecodeFromString(identity string) (SigningPubKey, error) - DecodeFromStringIntoBytes(identity string) ([]byte, error) -} - -// Below keys are required for testing and mocking purposes - -type EmptyRecorderEncryptionKey struct { - recordedEncrypted [][]byte - recordedDecrypted [][]byte -} - -func (f *EmptyRecorderEncryptionKey) Equals(key Key) bool { - return true -} - -func (f *EmptyRecorderEncryptionKey) Raw() ([]byte, error) { - panic("can't get bytes from this key") -} - -func (f *EmptyRecorderEncryptionKey) GetPublic() EncryptionPubKey { - panic("this key doesn't have a public key") -} - -func (f *EmptyRecorderEncryptionKey) Encrypt(msg []byte) ([]byte, error) { - f.recordedEncrypted = append(f.recordedEncrypted, msg) - return msg, nil -} - -func (f *EmptyRecorderEncryptionKey) Decrypt(msg []byte) ([]byte, error) { - f.recordedDecrypted = append(f.recordedDecrypted, msg) - return msg, nil -} - -type SignatureVerificationPayload struct { - message []byte - signature []byte -} - -type EmptyRecorderVerificationKey struct { - verifications []SignatureVerificationPayload -} - -func (e *EmptyRecorderVerificationKey) Bytes() ([]byte, error) { - panic("can't get bytes from this key") -} - -func (e *EmptyRecorderVerificationKey) Equals(key crypto.Key) bool { - return true -} - -func (e *EmptyRecorderVerificationKey) Raw() ([]byte, error) { - panic("can't get bytes from this key") -} - -func (e *EmptyRecorderVerificationKey) Type() crypto_pb.KeyType { - panic("can't get type from this key") -} - -func (e *EmptyRecorderVerificationKey) Verify(data []byte, sig []byte) (bool, error) { - e.verifications = append(e.verifications, SignatureVerificationPayload{ - message: data, - signature: sig, - }) - return true, nil -} - -func NewMockSigningPubKeyFromBytes(bytes []byte) (SigningPubKey, error) { - return &EmptyRecorderVerificationKey{}, nil -} diff --git a/util/keys/rsa.go b/util/keys/rsa.go new file mode 100644 index 00000000..254df7f6 --- /dev/null +++ b/util/keys/rsa.go @@ -0,0 +1,119 @@ +package keys + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha512" + "crypto/subtle" + "crypto/x509" + "errors" + "io" +) + +var MinRsaKeyBits = 2048 + +var ErrKeyLengthTooSmall = errors.New("error key length too small") + +type EncryptionRsaPrivKey struct { + privKey rsa.PrivateKey +} + +type EncryptionRsaPubKey struct { + pubKey rsa.PublicKey +} + +func (e *EncryptionRsaPubKey) Equals(key Key) bool { + other, ok := (key).(*EncryptionRsaPubKey) + if !ok { + return keyEquals(e, key) + } + + return e.pubKey.N.Cmp(other.pubKey.N) == 0 && e.pubKey.E == other.pubKey.E +} + +func (e *EncryptionRsaPubKey) Raw() ([]byte, error) { + return x509.MarshalPKIXPublicKey(&e.pubKey) +} + +func (e *EncryptionRsaPubKey) Encrypt(data []byte) ([]byte, error) { + hash := sha512.New() + return rsa.EncryptOAEP(hash, rand.Reader, &e.pubKey, data, nil) +} + +func (e *EncryptionRsaPrivKey) Equals(key Key) bool { + other, ok := (key).(*EncryptionRsaPrivKey) + if !ok { + return keyEquals(e, key) + } + + return e.privKey.N.Cmp(other.privKey.N) == 0 && e.privKey.E == other.privKey.E +} + +func (e *EncryptionRsaPrivKey) Raw() ([]byte, error) { + b := x509.MarshalPKCS1PrivateKey(&e.privKey) + return b, nil +} + +func (e *EncryptionRsaPrivKey) Decrypt(bytes []byte) ([]byte, error) { + hash := sha512.New() + return rsa.DecryptOAEP(hash, rand.Reader, &e.privKey, bytes, nil) +} + +func (e *EncryptionRsaPrivKey) GetPublic() EncryptionPubKey { + return &EncryptionRsaPubKey{pubKey: e.privKey.PublicKey} +} + +func GenerateRandomRSAKeyPair(bits int) (EncryptionPrivKey, EncryptionPubKey, error) { + return GenerateRSAKeyPair(bits, rand.Reader) +} + +func GenerateRSAKeyPair(bits int, src io.Reader) (EncryptionPrivKey, EncryptionPubKey, error) { + if bits < MinRsaKeyBits { + return nil, nil, ErrKeyLengthTooSmall + } + priv, err := rsa.GenerateKey(src, bits) + if err != nil { + return nil, nil, err + } + pk := priv.PublicKey + return &EncryptionRsaPrivKey{privKey: *priv}, &EncryptionRsaPubKey{pubKey: pk}, nil +} + +func NewEncryptionRsaPrivKeyFromBytes(bytes []byte) (EncryptionPrivKey, error) { + sk, err := x509.ParsePKCS1PrivateKey(bytes) + if err != nil { + return nil, err + } + if sk.N.BitLen() < MinRsaKeyBits { + return nil, ErrKeyLengthTooSmall + } + return &EncryptionRsaPrivKey{privKey: *sk}, nil +} + +func NewEncryptionRsaPubKeyFromBytes(bytes []byte) (EncryptionPubKey, error) { + pub, err := x509.ParsePKIXPublicKey(bytes) + if err != nil { + return nil, err + } + pk, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, errors.New("not actually an rsa public key") + } + if pk.N.BitLen() < MinRsaKeyBits { + return nil, ErrKeyLengthTooSmall + } + + return &EncryptionRsaPubKey{pubKey: *pk}, nil +} + +func keyEquals(k1, k2 Key) bool { + a, err := k1.Raw() + if err != nil { + return false + } + b, err := k2.Raw() + if err != nil { + return false + } + return subtle.ConstantTimeCompare(a, b) == 1 +} diff --git a/util/keys/signingkey.go b/util/keys/signingkey.go new file mode 100644 index 00000000..6c9af27d --- /dev/null +++ b/util/keys/signingkey.go @@ -0,0 +1,22 @@ +package keys + +type SigningPrivKey interface { + Key + + Sign([]byte) ([]byte, error) + + GetPublic() SigningPubKey +} + +type SigningPubKey interface { + Key + + Verify(data []byte, sig []byte) (bool, error) +} + +type SigningPubKeyDecoder interface { + DecodeFromBytes(bytes []byte) (SigningPubKey, error) + DecodeFromString(identity string) (SigningPubKey, error) + DecodeFromStringIntoBytes(identity string) ([]byte, error) + EncodeToString(pubkey SigningPubKey) (string, error) +}