├── cipher ├── ecdh │ ├── ecdh.go │ ├── errors.go │ ├── sr25519.go │ ├── secp256k1.go │ ├── ed25519.go │ └── ecdh_test.go ├── aes256cbc │ ├── aes256cbc.go │ ├── conversions.go │ ├── data.go │ ├── compression.go │ ├── ecdh.go │ ├── encoding.go │ ├── end_end_test.go │ ├── encrypter.go │ ├── compression_test.go │ ├── data_test.go │ ├── decrypter.go │ ├── conversions_test.go │ ├── decrypter_test.go │ └── encrypter_test.go ├── noop │ ├── docs.go │ ├── decrypter.go │ ├── encoding.go │ ├── encrypter.go │ ├── decrypter_test.go │ └── encrypter_test.go ├── errors.go ├── keyexchange.go ├── nacl │ ├── private_key.go │ ├── nacl.go │ ├── public_key_decrypter.go │ ├── keyexchange.go │ ├── public_key_encrypter.go │ ├── private_key_decrypter.go │ ├── private_key_encrypter.go │ ├── serialization.go │ ├── keyexchange_test.go │ ├── public_key_end_end_test.go │ ├── private_key_decrypter_test.go │ └── private_key_encrypter_test.go ├── encrypter │ ├── encrypter.go │ └── encrypter_test.go ├── cipher.go └── ciphertest │ ├── keyexchange_mock.go │ └── cipher_mock.go ├── mnemonic ├── mnemonic.go └── mnemonic_test.go ├── ed25519 ├── ed25519.go ├── public.go ├── derive.go ├── private.go ├── ed25519test │ └── keys.go ├── end_end_test.go ├── keys_test.go ├── public_test.go ├── derive_test.go └── private_test.go ├── factory ├── seed.go └── seed_test.go ├── public.go ├── private.go ├── encoding ├── messaging.go └── messaging_test.go ├── secp256k1 ├── ecdsa_keys_test.go ├── keys_test.go ├── secp256k1test │ └── keys.go ├── public.go ├── private.go └── end_end_test.go ├── chaincode ├── generate.go └── generate_test.go ├── go.mod ├── secp256r1 ├── public.go ├── roundtrip_test.go ├── secp256r1test │ └── keys.go ├── keys_test.go ├── private.go └── public_test.go ├── sr25519 ├── end_end_test.go ├── public.go ├── sign.go ├── keys_test.go ├── sr25519test │ └── keys.go ├── private.go └── public_test.go ├── multikey ├── private.go ├── ids.go ├── names.go ├── private_test.go ├── kind.go ├── public.go ├── ids_test.go ├── names_test.go ├── kind_test.go └── public_test.go ├── keys.go ├── internal └── schnorrkel │ └── secretkey.go ├── Readme.md └── cryptotest ├── public_mock.go └── private_mock.go /cipher/ecdh/ecdh.go: -------------------------------------------------------------------------------- 1 | // Package ecdh has implementations for different asymmetric key exchange. 2 | // Based on the Elliptic Curve Diffie-Hellman key agreement protocol. 3 | package ecdh 4 | -------------------------------------------------------------------------------- /cipher/aes256cbc/aes256cbc.go: -------------------------------------------------------------------------------- 1 | // Package aes256cbc implements Advanced Encryption Standard with a 256 bit key length, 2 | // using Chain Block Cipher mode (AES-256-CBC). 3 | package aes256cbc 4 | -------------------------------------------------------------------------------- /mnemonic/mnemonic.go: -------------------------------------------------------------------------------- 1 | package mnemonic 2 | 3 | import ( 4 | "crypto/sha512" 5 | 6 | "golang.org/x/crypto/pbkdf2" 7 | ) 8 | 9 | func ToSeed(mnemonic string, password string) []byte { 10 | return pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 32, sha512.New) 11 | } 12 | -------------------------------------------------------------------------------- /cipher/noop/docs.go: -------------------------------------------------------------------------------- 1 | // Package noop is a no operation encryption algorithm 2 | // for use when the message is intended to be publically readable. 3 | // 4 | // Noop implements the Encrypt and Decrypt interfaces to ensure that is is compatible 5 | // with Mailchain protocol. 6 | // It does not perform any encryption or decryption actions. 7 | package noop 8 | -------------------------------------------------------------------------------- /cipher/errors.go: -------------------------------------------------------------------------------- 1 | package cipher 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrEncrypt returns the error message if encryption failed 7 | // 8 | ErrEncrypt = errors.New("cipher: encryption failed") //nolint:gochecknoglobals 9 | // ErrDecrypt returns the error message if decryption failed 10 | // 11 | ErrDecrypt = errors.New("cipher: decryption failed") //nolint:gochecknoglobals 12 | ) 13 | -------------------------------------------------------------------------------- /cipher/ecdh/errors.go: -------------------------------------------------------------------------------- 1 | package ecdh 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrEphemeralGenerate generic error when failing to geneate ephemeral keys 7 | ErrEphemeralGenerate = errors.New("ecdh: ephemeral generation failed") 8 | // ErrSharedSecretGenerate generic error when failing to geneate a shared secret 9 | ErrSharedSecretGenerate = errors.New("ecdh: shared secret generation failed") 10 | ) 11 | -------------------------------------------------------------------------------- /cipher/noop/decrypter.go: -------------------------------------------------------------------------------- 1 | package noop 2 | 3 | import ( 4 | "github.com/mailchain/go-crypto/cipher" 5 | ) 6 | 7 | // NewDecrypter create a new decrypter attaching the private key to it 8 | func NewDecrypter() Decrypter { 9 | return Decrypter{} 10 | } 11 | 12 | // Decrypter will decrypt data using AES256CBC method 13 | type Decrypter struct { 14 | } 15 | 16 | // Decrypt data using recipient private key with AES in CBC mode. 17 | func (d Decrypter) Decrypt(data cipher.EncryptedContent) (cipher.PlainContent, error) { 18 | content, err := bytesDecode(data) 19 | return cipher.PlainContent(content), err 20 | } 21 | -------------------------------------------------------------------------------- /ed25519/ed25519.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import "golang.org/x/crypto/ed25519" 4 | 5 | const ( 6 | // PublicKeySize is the size, in bytes, of public keys as used in this package. 7 | PublicKeySize = ed25519.PublicKeySize 8 | // PrivateKeySize is the size, in bytes, of private keys as used in this package. 9 | PrivateKeySize = ed25519.PrivateKeySize 10 | // SignatureSize is the size, in bytes, of signatures generated and verified by this package. 11 | SignatureSize = ed25519.SignatureSize 12 | // SeedSize is the size, in bytes, of private key seeds. These are the private key representations used by RFC 8032. 13 | SeedSize = ed25519.SeedSize 14 | ) 15 | -------------------------------------------------------------------------------- /factory/seed.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | ) 7 | 8 | // NewSeed returns a cryptographically secure random seed. Random seeds are needed 9 | // when creating a new secure private key. 10 | 11 | // The length of the seed depends on it's usage. In most cases seed length is between 12 | // 16 and 64 making it (128 to 512 bits). 13 | func NewSeed(length uint8) ([]byte, error) { 14 | return generateSeed(rand.Reader, length) 15 | } 16 | 17 | func generateSeed(r io.Reader, length uint8) ([]byte, error) { 18 | buf := make([]byte, length) 19 | 20 | if _, err := r.Read(buf); err != nil { 21 | return nil, err 22 | } 23 | 24 | return buf, nil 25 | } 26 | -------------------------------------------------------------------------------- /public.go: -------------------------------------------------------------------------------- 1 | //go:generate mockgen -source=public.go -package=cryptotest -destination=./cryptotest/public_mock.go 2 | package crypto 3 | 4 | // PublicKey definition usable in all mailchain crypto operations 5 | type PublicKey interface { 6 | // Bytes returns the raw bytes representation of the public key. 7 | // 8 | // The returned bytes are used for encrypting, verifying a signature, and locating an address. 9 | Bytes() []byte 10 | // Verify verifies whether sig is a valid signature of message. 11 | Verify(message, sig []byte) bool 12 | } 13 | 14 | type ExtendedPublicKey interface { 15 | Bytes() []byte 16 | PublicKey() PublicKey 17 | Derive(index uint32) (ExtendedPublicKey, error) 18 | } 19 | -------------------------------------------------------------------------------- /private.go: -------------------------------------------------------------------------------- 1 | //go:generate mockgen -source=private.go -package=cryptotest -destination=./cryptotest/private_mock.go 2 | package crypto 3 | 4 | // PrivateKey definition usable in all mailchain crypto operations 5 | type PrivateKey interface { 6 | // Bytes returns the byte representation of the private key 7 | Bytes() []byte 8 | // PublicKey from the PrivateKey 9 | PublicKey() PublicKey 10 | // Sign signs the message with the key and returns the signature. 11 | Sign(message []byte) ([]byte, error) 12 | } 13 | 14 | type ExtendedPrivateKey interface { 15 | Bytes() []byte 16 | PrivateKey() PrivateKey 17 | Derive(index uint32) (ExtendedPrivateKey, error) 18 | ExtendedPublicKey() (ExtendedPublicKey, error) 19 | } 20 | -------------------------------------------------------------------------------- /cipher/noop/encoding.go: -------------------------------------------------------------------------------- 1 | package noop 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mailchain/go-crypto/cipher" 7 | ) 8 | 9 | // bytesEncode encode the encrypted data to the hex format 10 | func bytesEncode(data cipher.EncryptedContent) cipher.EncryptedContent { 11 | encodedData := make(cipher.EncryptedContent, 1+len(data)) 12 | encodedData[0] = cipher.NoOperation 13 | copy(encodedData[1:], data) 14 | 15 | return encodedData 16 | } 17 | 18 | // bytesDecode convert the hex format in to the encrypted data format 19 | func bytesDecode(raw cipher.EncryptedContent) (cipher.EncryptedContent, error) { 20 | if raw[0] != cipher.NoOperation { 21 | return nil, fmt.Errorf("invalid prefix") 22 | } 23 | 24 | return raw[1:], nil 25 | } 26 | -------------------------------------------------------------------------------- /ed25519/public.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/crypto/ed25519" 7 | ) 8 | 9 | // PublicKey based on the ed25519 curve 10 | type PublicKey struct { 11 | Key ed25519.PublicKey 12 | } 13 | 14 | // Verify verifies whether sig is a valid signature of message. 15 | func (pk PublicKey) Verify(message, sig []byte) bool { 16 | return ed25519.Verify(pk.Key, message, sig) 17 | } 18 | 19 | // Bytes returns the byte representation of the public key 20 | func (pk PublicKey) Bytes() []byte { 21 | return pk.Key 22 | } 23 | 24 | // PublicKeyFromBytes create a public key from []byte 25 | func PublicKeyFromBytes(keyBytes []byte) (*PublicKey, error) { 26 | if len(keyBytes) != ed25519.PublicKeySize { 27 | return nil, fmt.Errorf("public key must be 32 bytes") 28 | } 29 | 30 | return &PublicKey{Key: keyBytes}, nil 31 | } 32 | -------------------------------------------------------------------------------- /cipher/aes256cbc/conversions.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ethereum/go-ethereum/crypto/ecies" 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/secp256k1" 9 | ) 10 | 11 | func asPublicECIES(pk crypto.PublicKey) (*ecies.PublicKey, error) { 12 | switch rpk := pk.(type) { 13 | case *secp256k1.PublicKey: 14 | return rpk.ECIES() 15 | case secp256k1.PublicKey: 16 | return rpk.ECIES() 17 | default: 18 | return nil, fmt.Errorf("could not convert public key") 19 | } 20 | } 21 | func asPrivateECIES(pk crypto.PrivateKey) (*ecies.PrivateKey, error) { 22 | switch rpk := pk.(type) { 23 | case *secp256k1.PrivateKey: 24 | return rpk.ECIES(), nil 25 | case secp256k1.PrivateKey: 26 | return rpk.ECIES(), nil 27 | default: 28 | return nil, fmt.Errorf("could not convert private key") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cipher/aes256cbc/data.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import "errors" 4 | 5 | type encryptedData struct { 6 | InitializationVector []byte `json:"iv"` 7 | EphemeralPublicKey []byte `json:"ephemPublicKey"` 8 | Ciphertext []byte `json:"ciphertext"` 9 | MessageAuthenticationCode []byte `json:"mac"` 10 | } 11 | 12 | func (e *encryptedData) verify() error { 13 | if len(e.InitializationVector) != 16 { 14 | return errors.New("`InitializationVector` must be 16") 15 | } 16 | 17 | if len(e.EphemeralPublicKey) != 65 { 18 | return errors.New("`EphemeralPublicKey` must be 65") 19 | } 20 | 21 | if len(e.MessageAuthenticationCode) != 32 { 22 | return errors.New("`MessageAuthenticationCode` must be 16") 23 | } 24 | 25 | if len(e.Ciphertext) == 0 { 26 | return errors.New("`Ciphertext` must not be empty") 27 | } 28 | 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /encoding/messaging.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/multikey" 8 | "github.com/mailchain/go-encoding" 9 | ) 10 | 11 | // PrefixMsgKey creates a prefix of MsgKey that is used in front of the mesage key. 12 | var PrefixMsgKey = []byte{0x1f, 0xf8, 0x39, 0xbf, 0x85, 0x99} //nolint: gochecknoglobals 13 | 14 | // EncodeMessagingPublicKey encodes a public key with prefix that indicates it's purpose. 15 | func EncodeMessagingPublicKey(key crypto.PublicKey) (string, error) { 16 | descriptiveKey, err := multikey.DescriptiveBytesFromPublicKey(key) 17 | if err != nil { 18 | return "", err 19 | } 20 | if descriptiveKey[0] == crypto.IDSECP256K1 { 21 | return "", errors.New("secp256k1 not supported") 22 | } 23 | 24 | return encoding.EncodeBase58(append(PrefixMsgKey, descriptiveKey...)), nil 25 | } 26 | -------------------------------------------------------------------------------- /secp256k1/ecdsa_keys_test.go: -------------------------------------------------------------------------------- 1 | package secp256k1 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "encoding/hex" 6 | "log" 7 | 8 | "github.com/ethereum/go-ethereum/crypto" 9 | ) 10 | 11 | func ecdsaPrivateKeyAlice() ecdsa.PrivateKey { 12 | b, _ := hex.DecodeString("01901E63389EF02EAA7C5782E08B40D98FAEF835F28BD144EECF5614A415943F") 13 | key, err := crypto.ToECDSA(b) 14 | if err != nil { 15 | log.Fatal(err) 16 | } 17 | return *key 18 | } 19 | 20 | func ecdsaPublicKeyAlice() ecdsa.PublicKey { 21 | return ecdsaPrivateKeyAlice().PublicKey 22 | } 23 | 24 | func ecdsaPrivateKeyBob() ecdsa.PrivateKey { 25 | b, _ := hex.DecodeString("DF4BA9F6106AD2846472F759476535E55C5805D8337DF5A11C3B139F438B98B3") 26 | key, err := crypto.ToECDSA(b) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | return *key 31 | } 32 | 33 | func ecdsaPublicKeyBob() ecdsa.PublicKey { 34 | return ecdsaPrivateKeyBob().PublicKey 35 | } 36 | -------------------------------------------------------------------------------- /ed25519/derive.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/minio/blake2b-simd" 8 | ) 9 | 10 | var hdkd = []byte{44, 69, 100, 50, 53, 53, 49, 57, 72, 68, 75, 68} //Ed25519HDKD prefix compatible with polkadot HDKD 11 | 12 | func DeriveHardenedKey(parent crypto.PrivateKey, chaincode []byte) (*PrivateKey, error) { 13 | parentSeed, err := seedBytes(parent) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | val := append(hdkd, parentSeed...) 19 | val = append(val, chaincode...) 20 | 21 | childSeed := blake2b.Sum256(val) 22 | 23 | return PrivateKeyFromBytes(childSeed[:]) 24 | } 25 | 26 | func seedBytes(parent crypto.PrivateKey) ([]byte, error) { 27 | switch edKey := parent.(type) { 28 | case *PrivateKey: 29 | return edKey.Key.Seed(), nil 30 | default: 31 | return nil, errors.New("unknown private key type") 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /mnemonic/mnemonic_test.go: -------------------------------------------------------------------------------- 1 | package mnemonic 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestToSeed(t *testing.T) { 10 | type args struct { 11 | mnemonic string 12 | password string 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want []byte 18 | }{ 19 | { 20 | "deputy other", 21 | args{ 22 | "deputy other grain consider empty next inform myself combine dish parent maple priority outdoor inherit lonely battle add humble jar silly tank item balance", 23 | "", 24 | }, 25 | []byte{196, 61, 147, 66, 207, 131, 22, 179, 98, 3, 83, 23, 116, 171, 96, 65, 14, 243, 147, 40, 21, 137, 42, 185, 147, 169, 115, 33, 38, 53, 82, 88}, 26 | }, 27 | } 28 | for _, tt := range tests { 29 | t.Run(tt.name, func(t *testing.T) { 30 | assert.Equal(t, tt.want, ToSeed(tt.args.mnemonic, tt.args.password)) 31 | }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chaincode/generate.go: -------------------------------------------------------------------------------- 1 | package chaincode 2 | 3 | import ( 4 | "encoding/binary" 5 | 6 | "golang.org/x/crypto/blake2b" 7 | ) 8 | 9 | func ChainCodeFromDeriveIndexBytes(input []byte) []byte { 10 | if len(input) == 32 { 11 | return input 12 | } 13 | if len(input) < 32 { 14 | // empty padding at the end 15 | var returnValue = make([]byte, 32) 16 | copy(returnValue, input) 17 | return returnValue 18 | } 19 | 20 | res := blake2b.Sum256(input) 21 | return res[:] 22 | } 23 | 24 | func ChainCodeFromDeriveIndexUint64(input uint64) []byte { 25 | var bytes [8]byte 26 | binary.LittleEndian.PutUint64(bytes[:], input) 27 | return ChainCodeFromDeriveIndexBytes(bytes[:]) 28 | } 29 | 30 | func ChainCodeFromDeriveIndexString(input string) []byte { 31 | val := []byte(input) 32 | len := byte(len(val) << 2) // add bitwise shifted length prefix 33 | return ChainCodeFromDeriveIndexBytes(append([]byte{len}, val...)) 34 | } 35 | -------------------------------------------------------------------------------- /cipher/keyexchange.go: -------------------------------------------------------------------------------- 1 | package cipher 2 | 3 | import "github.com/mailchain/go-crypto" 4 | 5 | //go:generate mockgen -source=keyexchange.go -package=ciphertest -destination=./ciphertest/keyexchange_mock.go 6 | 7 | // KeyExchange agrees on a symmetric keys by performing a key exchange using asymmetric keys. 8 | type KeyExchange interface { 9 | // EphemeralKey generates a private/public key pair. 10 | EphemeralKey() (private crypto.PrivateKey, err error) 11 | 12 | // SharedSecret computes a secret value from a private / public key pair. 13 | // On sending a message the private key should be an ephemeralKey or generated private key, 14 | // the public key is the recipient public key. 15 | // On reading a message the private key is the recipient private key, the public key is the 16 | // ephemeralKey or generated public key. 17 | SharedSecret(privateKey crypto.PrivateKey, publicKey crypto.PublicKey) ([]byte, error) 18 | } 19 | -------------------------------------------------------------------------------- /cipher/nacl/private_key.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/agl/ed25519/extra25519" 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/ed25519" 9 | "github.com/mailchain/go-crypto/secp256k1" 10 | "github.com/mailchain/go-crypto/sr25519" 11 | ) 12 | 13 | func encryptionKeyBytes(privateKey crypto.PrivateKey) ([]byte, error) { 14 | switch key := privateKey.(type) { 15 | case *ed25519.PrivateKey, ed25519.PrivateKey: 16 | var ed25519Key [64]byte 17 | var out [32]byte 18 | copy(ed25519Key[:], key.Bytes()) 19 | extra25519.PrivateKeyToCurve25519(&out, &ed25519Key) 20 | 21 | return out[:], nil 22 | case *secp256k1.PrivateKey, secp256k1.PrivateKey: 23 | return key.Bytes(), nil 24 | case *sr25519.PrivateKey, sr25519.PrivateKey: 25 | return nil, errors.New("sr25519 private keys are not supported") 26 | default: 27 | return nil, errors.New("unknown private key type") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cipher/noop/encrypter.go: -------------------------------------------------------------------------------- 1 | package noop 2 | 3 | import ( 4 | "github.com/mailchain/go-crypto" 5 | "github.com/mailchain/go-crypto/cipher" 6 | ) 7 | 8 | // NewEncrypter create a new encrypter with crypto rand for reader 9 | func NewEncrypter(pubKey crypto.PublicKey) (*Encrypter, error) { 10 | return &Encrypter{publicKey: pubKey}, nil 11 | } 12 | 13 | // Encrypter will not perform any operation when encrypting the message. 14 | // 15 | // No operation (noop) encrypter is used when the contents of the message 16 | // and envelope are intended to readable by the public. 17 | type Encrypter struct { 18 | publicKey crypto.PublicKey 19 | } 20 | 21 | // Encrypt does not apply any encrption algortim. 22 | // PlainContent will be return as EncryptedContent with the encryption method 23 | // prepend as the first byte. 24 | func (e Encrypter) Encrypt(message cipher.PlainContent) (cipher.EncryptedContent, error) { 25 | return bytesEncode(cipher.EncryptedContent(message)), nil 26 | } 27 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mailchain/go-crypto 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 7 | github.com/andreburgaud/crypt2go v1.1.0 8 | github.com/ethereum/go-ethereum v1.12.1 9 | github.com/golang/mock v1.6.0 10 | github.com/gtank/merlin v0.1.1 11 | github.com/gtank/ristretto255 v0.1.2 12 | github.com/mailchain/go-encoding v0.0.0-20221027160803-899f9dcab49d 13 | github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 14 | github.com/stretchr/testify v1.8.1 15 | golang.org/x/crypto v0.17.0 16 | ) 17 | 18 | require ( 19 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect 20 | github.com/davecgh/go-spew v1.1.1 // indirect 21 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect 22 | github.com/holiman/uint256 v1.2.3 // indirect 23 | github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect 24 | github.com/mr-tron/base58 v1.2.0 // indirect 25 | github.com/pmezard/go-difflib v1.0.0 // indirect 26 | golang.org/x/sys v0.15.0 // indirect 27 | gopkg.in/yaml.v3 v3.0.1 // indirect 28 | ) 29 | -------------------------------------------------------------------------------- /cipher/aes256cbc/compression.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "crypto/elliptic" 5 | "fmt" 6 | 7 | "github.com/ethereum/go-ethereum/crypto/ecies" 8 | "github.com/ethereum/go-ethereum/crypto/secp256k1" 9 | ) 10 | 11 | const ( 12 | pubKeyBytesLenCompressed = 33 13 | pubKeyBytesLenUncompressed = 65 14 | compressedKeyPrefix = 4 15 | ) 16 | 17 | // compress a 65 byte uncompressed public key 18 | func compress(publicKey []byte) ([]byte, error) { 19 | if len(publicKey) == pubKeyBytesLenUncompressed-1 && publicKey[0] != compressedKeyPrefix { 20 | publicKey = append([]byte{compressedKeyPrefix}, publicKey...) 21 | } 22 | if len(publicKey) != pubKeyBytesLenUncompressed { 23 | return nil, fmt.Errorf("length of uncompressed public key is invalid") 24 | } 25 | x, y := elliptic.Unmarshal(ecies.DefaultCurve, publicKey) 26 | 27 | return secp256k1.CompressPubkey(x, y), nil 28 | } 29 | 30 | // decompress a 33 byte compressed public key 31 | func decompress(publicKey []byte) []byte { 32 | x, y := secp256k1.DecompressPubkey(publicKey) 33 | return elliptic.Marshal(ecies.DefaultCurve, x, y) 34 | } 35 | -------------------------------------------------------------------------------- /secp256r1/public.go: -------------------------------------------------------------------------------- 1 | package secp256r1 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "crypto/ecdsa" 8 | "crypto/elliptic" 9 | 10 | "github.com/mailchain/go-crypto" 11 | ) 12 | 13 | // PublicKey based on the p256 curve 14 | type PublicKey struct { 15 | Key ecdsa.PublicKey 16 | } 17 | 18 | // Verify verifies whether sig is a valid signature of message. 19 | func (pk PublicKey) Verify(message, sig []byte) bool { 20 | r := new(big.Int).SetBytes(sig[:32]) 21 | s := new(big.Int).SetBytes(sig[32:]) 22 | return ecdsa.Verify(&pk.Key, message, r, s) 23 | } 24 | 25 | // Bytes returns the byte representation of the public key 26 | func (pk PublicKey) Bytes() []byte { 27 | return elliptic.MarshalCompressed(elliptic.P256(), pk.Key.X, pk.Key.Y) 28 | } 29 | 30 | // PublicKeyFromBytes create a public key from []byte 31 | func PublicKeyFromBytes(keyBytes []byte) (crypto.PublicKey, error) { 32 | if len(keyBytes) != 33 { 33 | return nil, fmt.Errorf("public key must be 33 bytes") 34 | } 35 | key := ecdsa.PublicKey{Curve: elliptic.P256()} 36 | key.X, key.Y = elliptic.UnmarshalCompressed(elliptic.P256(), keyBytes) 37 | 38 | return &PublicKey{Key: key}, nil 39 | } 40 | -------------------------------------------------------------------------------- /cipher/encrypter/encrypter.go: -------------------------------------------------------------------------------- 1 | package encrypter 2 | 3 | import ( 4 | "fmt" 5 | 6 | keys "github.com/mailchain/go-crypto" 7 | crypto "github.com/mailchain/go-crypto/cipher" 8 | "github.com/mailchain/go-crypto/cipher/aes256cbc" 9 | "github.com/mailchain/go-crypto/cipher/nacl" 10 | "github.com/mailchain/go-crypto/cipher/noop" 11 | ) 12 | 13 | // Cipher Name lookup 14 | const ( 15 | // NoOperation encryption type name. 16 | NoOperation string = "noop" 17 | // NACL encryption type name. 18 | NACLECDH string = "nacl-ecdh" 19 | // AES256CBC encryption type name. 20 | AES256CBC string = "aes256cbc" 21 | ) 22 | 23 | // GetEncrypter is an `Encrypter` factory that returns an encrypter 24 | func GetEncrypter(encryption string, pubKey keys.PublicKey) (crypto.Encrypter, error) { 25 | switch encryption { 26 | case AES256CBC: 27 | return aes256cbc.NewEncrypter(pubKey) 28 | case NACLECDH: 29 | return nacl.NewPublicKeyEncrypter(pubKey) 30 | case NoOperation: 31 | return noop.NewEncrypter(pubKey) 32 | case "": 33 | return nil, fmt.Errorf("`encryption` provided is set to empty") 34 | default: 35 | return nil, fmt.Errorf("`encryption` provided is invalid") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cipher/nacl/nacl.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | 7 | "golang.org/x/crypto/nacl/secretbox" 8 | ) 9 | 10 | const nonceSize = 24 11 | const secretKeySize = 32 12 | 13 | func easyOpen(box, key []byte) ([]byte, error) { 14 | var secretKey [secretKeySize]byte 15 | 16 | if len(key) != secretKeySize { 17 | return nil, errors.New("secretbox: key length must be 32") 18 | } 19 | 20 | if len(box) < nonceSize { 21 | return nil, errors.New("secretbox: message too short") 22 | } 23 | 24 | decryptNonce := new([nonceSize]byte) 25 | copy(decryptNonce[:], box[:nonceSize]) 26 | copy(secretKey[:], key) 27 | 28 | decrypted, ok := secretbox.Open([]byte{}, box[nonceSize:], decryptNonce, &secretKey) 29 | if !ok { 30 | return nil, errors.New("secretbox: could not decrypt data with private key") 31 | } 32 | 33 | return decrypted, nil 34 | } 35 | 36 | func easySeal(message, key []byte, rand io.Reader) ([]byte, error) { 37 | nonce := new([nonceSize]byte) 38 | if _, err := rand.Read(nonce[:]); err != nil { 39 | return nil, err 40 | } 41 | 42 | var secretKey [secretKeySize]byte 43 | 44 | copy(secretKey[:], key) 45 | 46 | return secretbox.Seal(nonce[:], message, nonce, &secretKey), nil 47 | } 48 | -------------------------------------------------------------------------------- /cipher/nacl/public_key_decrypter.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "github.com/mailchain/go-crypto" 5 | "github.com/mailchain/go-crypto/cipher" 6 | ) 7 | 8 | // NewPublicKeyDecrypter create a new decrypter attaching the private key to it 9 | func NewPublicKeyDecrypter(privateKey crypto.PrivateKey) (*PublicKeyDecrypter, error) { 10 | keyExchange, err := getPrivateKeyExchange(privateKey) 11 | if err != nil { 12 | return nil, err 13 | } 14 | 15 | return &PublicKeyDecrypter{privateKey: privateKey, keyExchange: keyExchange}, nil 16 | } 17 | 18 | // PublicKeyDecrypter will decrypt data using NACL with ECDH key exchange 19 | type PublicKeyDecrypter struct { 20 | privateKey crypto.PrivateKey 21 | keyExchange cipher.KeyExchange 22 | } 23 | 24 | // Decrypt data using recipient private key with AES in CBC mode. 25 | func (d PublicKeyDecrypter) Decrypt(data cipher.EncryptedContent) (cipher.PlainContent, error) { 26 | data, pubKey, err := deserializePublicKeyEncryptedContent(data) 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | sharedSecret, err := d.keyExchange.SharedSecret(d.privateKey, pubKey) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | return easyOpen(data, sharedSecret) 37 | } 38 | -------------------------------------------------------------------------------- /factory/seed_test.go: -------------------------------------------------------------------------------- 1 | package factory 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "testing" 8 | "testing/iotest" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func Test_generateSeed(t *testing.T) { 14 | type args struct { 15 | r io.Reader 16 | length uint8 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | want []byte 22 | wantErr bool 23 | }{ 24 | { 25 | "success", 26 | args{ 27 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 28 | 16, 29 | }, 30 | []byte{0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50}, 31 | false, 32 | }, 33 | { 34 | "err", 35 | args{ 36 | iotest.ErrReader(errors.New("error")), 37 | 255, 38 | }, 39 | nil, 40 | true, 41 | }, 42 | } 43 | for _, tt := range tests { 44 | t.Run(tt.name, func(t *testing.T) { 45 | got, err := generateSeed(tt.args.r, tt.args.length) 46 | if (err != nil) != tt.wantErr { 47 | t.Errorf("generateSeed() error = %v, wantErr %v", err, tt.wantErr) 48 | return 49 | } 50 | if !assert.Equal(t, tt.want, got) { 51 | t.Errorf("generateSeed() = %v, want %v", got, tt.want) 52 | } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sr25519/end_end_test.go: -------------------------------------------------------------------------------- 1 | package sr25519 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSignVerify(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | signedBy PrivateKey 13 | verifiedBy PublicKey 14 | message []byte 15 | wantErr bool 16 | wantVerified bool 17 | }{ 18 | { 19 | "bob-private-public-key", 20 | bobPrivateKey, 21 | bobPublicKey, 22 | []byte("message"), 23 | false, 24 | true, 25 | }, 26 | { 27 | "alice-private-public-key", 28 | alicePrivateKey, 29 | alicePublicKey, 30 | []byte("egassem"), 31 | false, 32 | true, 33 | }, 34 | { 35 | "alice-private-bob-public-key", 36 | alicePrivateKey, 37 | bobPublicKey, 38 | []byte("egassem"), 39 | false, 40 | false, 41 | }, 42 | { 43 | "bob-private-alice-public-key", 44 | bobPrivateKey, 45 | alicePublicKey, 46 | []byte("message"), 47 | false, 48 | false, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | gotSig, err := tt.signedBy.Sign(tt.message) 54 | assert.Equal(t, tt.wantErr, err != nil) 55 | verified := tt.verifiedBy.Verify(tt.message, gotSig) 56 | assert.Equal(t, tt.wantVerified, verified) 57 | }) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cipher/nacl/keyexchange.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | "github.com/mailchain/go-crypto/cipher/ecdh" 10 | "github.com/mailchain/go-crypto/ed25519" 11 | "github.com/mailchain/go-crypto/secp256k1" 12 | "github.com/mailchain/go-crypto/sr25519" 13 | ) 14 | 15 | func getPublicKeyExchange(recipientPublicKey crypto.PublicKey) (cipher.KeyExchange, error) { 16 | switch recipientPublicKey.(type) { 17 | case ed25519.PublicKey, *ed25519.PublicKey: 18 | return ecdh.NewED25519(rand.Reader) 19 | case sr25519.PublicKey, *sr25519.PublicKey: 20 | return ecdh.NewSR25519(rand.Reader) 21 | case secp256k1.PublicKey, *secp256k1.PublicKey: 22 | return ecdh.NewSECP256K1(rand.Reader) 23 | default: 24 | return nil, fmt.Errorf("invalid public key type for nacl encryption") 25 | } 26 | } 27 | 28 | func getPrivateKeyExchange(pk crypto.PrivateKey) (cipher.KeyExchange, error) { 29 | switch pk.(type) { 30 | case *ed25519.PrivateKey: 31 | return ecdh.NewED25519(rand.Reader) 32 | case *sr25519.PrivateKey: 33 | return ecdh.NewSR25519(rand.Reader) 34 | case *secp256k1.PrivateKey: 35 | return ecdh.NewSECP256K1(rand.Reader) 36 | default: 37 | return nil, fmt.Errorf("invalid private key type for nacl decryption") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /multikey/private.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519" 8 | "github.com/mailchain/go-crypto/secp256k1" 9 | "github.com/mailchain/go-crypto/secp256r1" 10 | "github.com/mailchain/go-crypto/sr25519" 11 | ) 12 | 13 | // PrivateKeyFromBytes returns a private key from `[]byte`. 14 | // 15 | // The function used to create the private key is based on the key type. 16 | // Supported key types are secp256k1, ed25519. 17 | func PrivateKeyFromBytes(keyType string, data []byte) (crypto.PrivateKey, error) { 18 | switch keyType { 19 | case crypto.KindSECP256K1: 20 | return secp256k1.PrivateKeyFromBytes(data) 21 | case crypto.KindED25519: 22 | return ed25519.PrivateKeyFromBytes(data) 23 | case crypto.KindSR25519: 24 | return sr25519.PrivateKeyFromBytes(data) 25 | case crypto.KindSECP256R1: 26 | return secp256r1.PrivateKeyFromBytes(data) 27 | default: 28 | return nil, fmt.Errorf("unsupported key type: %q", keyType) 29 | } 30 | } 31 | 32 | func DescriptiveBytesFromPrivateKey(in crypto.PrivateKey) ([]byte, error) { 33 | idByte, err := IDFromPrivateKey(in) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | out := make([]byte, len(in.Bytes())+1) 39 | out[0] = idByte 40 | copy(out[1:], in.Bytes()) 41 | 42 | return out, nil 43 | } 44 | -------------------------------------------------------------------------------- /keys.go: -------------------------------------------------------------------------------- 1 | package crypto 2 | 3 | const ( 4 | // KindSECP256K1 string identifier for secp256k1 keys. 5 | KindSECP256K1 = "secp256k1" 6 | // KindSECP256R1 string identifier for secp256r1 keys. 7 | KindSECP256R1 = "secp256r1" 8 | // KindED25519 string identifier for ed25519 keys. 9 | KindED25519 = "ed25519" 10 | // KindSR25519 string identifier for sr25519 keys. 11 | KindSR25519 = "sr25519" 12 | ) 13 | 14 | const ( 15 | // IDUnknown byte identifier for unknown keys. 16 | IDUnknown = 0x0 17 | // IDSECP256K1 byte identifier for secp256k1 keys. 18 | IDSECP256K1 = 0xe1 19 | // IDED25519 byte identifier for ed25519 keys. 20 | IDED25519 = 0xe2 21 | // IDSR25519 byte identifier for sr25519 keys. 22 | IDSR25519 = 0xe3 23 | // IDSECP256R1 Id identifier for secp256r1 keys. 24 | IDSECP256R1 = 0xe4 25 | // IDNonSpecified Id identifier for non specified secret keys. 26 | IDNonSpecified = 0xee 27 | ) 28 | 29 | var CurveKindIDMapping = map[string]byte{ //nolint:gochecknoglobals 30 | KindSECP256K1: IDSECP256K1, 31 | KindED25519: IDED25519, 32 | KindSR25519: IDSR25519, 33 | KindSECP256R1: IDSECP256R1, 34 | } 35 | 36 | // KeyTypes available key types. 37 | func KeyTypes() map[string]bool { 38 | return map[string]bool{ 39 | KindSECP256K1: true, 40 | KindED25519: true, 41 | KindSR25519: true, 42 | KindSECP256R1: true, 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /multikey/ids.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519" 8 | "github.com/mailchain/go-crypto/secp256k1" 9 | "github.com/mailchain/go-crypto/secp256r1" 10 | "github.com/mailchain/go-crypto/sr25519" 11 | ) 12 | 13 | func IDFromPublicKey(key crypto.PublicKey) (byte, error) { 14 | switch key.(type) { 15 | case *ed25519.PublicKey, ed25519.PublicKey: 16 | return crypto.IDED25519, nil 17 | case *secp256k1.PublicKey, secp256k1.PublicKey: 18 | return crypto.IDSECP256K1, nil 19 | case *secp256r1.PublicKey, secp256r1.PublicKey: 20 | return crypto.IDSECP256R1, nil 21 | case *sr25519.PublicKey, sr25519.PublicKey: 22 | return crypto.IDSR25519, nil 23 | default: 24 | return crypto.IDUnknown, errors.New("unknown public key type") 25 | } 26 | } 27 | 28 | func IDFromPrivateKey(key crypto.PrivateKey) (byte, error) { 29 | switch key.(type) { 30 | case *ed25519.PrivateKey, ed25519.PrivateKey: 31 | return crypto.IDED25519, nil 32 | case *secp256k1.PrivateKey, secp256k1.PrivateKey: 33 | return crypto.IDSECP256K1, nil 34 | case *secp256r1.PrivateKey, secp256r1.PrivateKey: 35 | return crypto.IDSECP256R1, nil 36 | case *sr25519.PrivateKey, sr25519.PrivateKey: 37 | return crypto.IDSR25519, nil 38 | default: 39 | return crypto.IDUnknown, errors.New("unknown private key type") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /multikey/names.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519" 8 | "github.com/mailchain/go-crypto/secp256k1" 9 | "github.com/mailchain/go-crypto/secp256r1" 10 | "github.com/mailchain/go-crypto/sr25519" 11 | ) 12 | 13 | func KindFromPublicKey(key crypto.PublicKey) (string, error) { 14 | switch key.(type) { 15 | case *ed25519.PublicKey, ed25519.PublicKey: 16 | return crypto.KindED25519, nil 17 | case *secp256k1.PublicKey, secp256k1.PublicKey: 18 | return crypto.KindSECP256K1, nil 19 | case *sr25519.PublicKey, sr25519.PublicKey: 20 | return crypto.KindSR25519, nil 21 | case *secp256r1.PublicKey, secp256r1.PublicKey: 22 | return crypto.KindSECP256R1, nil 23 | default: 24 | return "", errors.New("unknown public key type") 25 | } 26 | } 27 | 28 | func KindFromPrivateKey(key crypto.PrivateKey) (string, error) { 29 | switch key.(type) { 30 | case *ed25519.PrivateKey, ed25519.PrivateKey: 31 | return crypto.KindED25519, nil 32 | case *secp256k1.PrivateKey, secp256k1.PrivateKey: 33 | return crypto.KindSECP256K1, nil 34 | case *sr25519.PrivateKey, sr25519.PrivateKey: 35 | return crypto.KindSR25519, nil 36 | case *secp256r1.PrivateKey, secp256r1.PrivateKey: 37 | return crypto.KindSECP256R1, nil 38 | default: 39 | return "", errors.New("unknown private key type") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /internal/schnorrkel/secretkey.go: -------------------------------------------------------------------------------- 1 | package schnorrkel 2 | 3 | import "crypto/sha512" 4 | 5 | // SecretKey consists of a secret scalar and a signing nonce 6 | type SecretKey struct { 7 | seed [32]byte 8 | key [32]byte 9 | nonce [32]byte 10 | } 11 | 12 | func (sk *SecretKey) Key() []byte { 13 | return sk.key[:] 14 | } 15 | 16 | func (sk *SecretKey) Seed() []byte { 17 | return sk.seed[:] 18 | } 19 | 20 | func (sk *SecretKey) Nonce() []byte { 21 | return sk.nonce[:] 22 | } 23 | 24 | func NewSecretKeySR25519(seed [32]byte) SecretKey { 25 | key := [32]byte{} 26 | nonce := [32]byte{} 27 | h := sha512.Sum512(seed[:]) 28 | 29 | copy(key[:], h[:32]) 30 | 31 | key[0] &= 248 32 | key[31] &= 63 33 | key[31] |= 64 34 | t := divideScalarByCofactor(key[:]) 35 | 36 | copy(key[:], t) 37 | copy(nonce[:], h[32:]) 38 | 39 | return SecretKey{seed: seed, key: key, nonce: nonce} 40 | } 41 | 42 | // https://github.com/w3f/schnorrkel/blob/718678e51006d84c7d8e4b6cde758906172e74f8/src/scalars.rs#L18 43 | func divideScalarByCofactor(s []byte) []byte { 44 | l := len(s) - 1 //nolint: gomnd length-1 45 | low := byte(0) 46 | 47 | for i := range s { 48 | r := s[l-i] & 0x07 // 0x07 == 0b00000110 49 | s[l-i] >>= 3 50 | s[l-i] += low 51 | low = r << 5 //nolint: gomnd https://github.com/w3f/schnorrkel/blob/718678e51006d84c7d8e4b6cde758906172e74f8/src/scalars.rs#L34 52 | } 53 | 54 | return s 55 | } 56 | -------------------------------------------------------------------------------- /secp256r1/roundtrip_test.go: -------------------------------------------------------------------------------- 1 | package secp256r1_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | 8 | "github.com/mailchain/go-crypto/secp256r1/secp256r1test" 9 | "github.com/stretchr/testify/assert" 10 | "golang.org/x/crypto/blake2b" 11 | ) 12 | 13 | func TestSignVerify(t *testing.T) { 14 | type args struct { 15 | signingKey crypto.PrivateKey 16 | verifyingKey crypto.PublicKey 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | want bool 22 | assertion assert.ErrorAssertionFunc 23 | }{ 24 | { 25 | "alice", 26 | args{ 27 | secp256r1test.AlicePrivateKey, 28 | secp256r1test.AlicePublicKey, 29 | }, 30 | true, 31 | assert.NoError, 32 | }, 33 | { 34 | "bob", 35 | args{ 36 | secp256r1test.BobPrivateKey, 37 | secp256r1test.BobPublicKey, 38 | }, 39 | true, 40 | assert.NoError, 41 | }, 42 | { 43 | "carlos", 44 | args{ 45 | secp256r1test.CarlosPrivateKey, 46 | secp256r1test.CarlosPublicKey, 47 | }, 48 | true, 49 | assert.NoError, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | digest := blake2b.Sum256([]byte("test message")) 55 | signature, err := tt.args.signingKey.Sign(digest[:]) 56 | tt.assertion(t, err) 57 | 58 | verified := tt.args.verifyingKey.Verify(digest[:], signature) 59 | assert.Equal(t, tt.want, verified) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /cipher/nacl/public_key_encrypter.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | ) 10 | 11 | // NewPublicKeyEncrypter creates a new encrypter with crypto rand for reader, 12 | // and attaching the public key to the encrypter. 13 | func NewPublicKeyEncrypter(publicKey crypto.PublicKey) (*PublicKeyEncrypter, error) { 14 | keyExchange, err := getPublicKeyExchange(publicKey) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return &PublicKeyEncrypter{rand: rand.Reader, publicKey: publicKey, keyExchange: keyExchange}, nil 20 | } 21 | 22 | // PublicKeyEncrypter will encrypt data using AES256CBC method. 23 | type PublicKeyEncrypter struct { 24 | rand io.Reader 25 | publicKey crypto.PublicKey 26 | keyExchange cipher.KeyExchange 27 | } 28 | 29 | // Encrypt encrypts the message with the key that was attached to it. 30 | func (e PublicKeyEncrypter) Encrypt(message cipher.PlainContent) (cipher.EncryptedContent, error) { 31 | ephemeralKey, err := e.keyExchange.EphemeralKey() 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | sharedSecret, err := e.keyExchange.SharedSecret(ephemeralKey, e.publicKey) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | encrypted, err := easySeal(message, sharedSecret, e.rand) 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return serializePublicKeyEncryptedContent(encrypted, ephemeralKey.PublicKey()) 47 | } 48 | -------------------------------------------------------------------------------- /cipher/nacl/private_key_decrypter.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/cipher" 8 | "github.com/mailchain/go-crypto/multikey" 9 | ) 10 | 11 | // NewPrivateKeyDecrypter create a new decrypter attaching the private key to it 12 | func NewPrivateKeyDecrypter(privateKey crypto.PrivateKey) (*PrivateKeyDecrypter, error) { 13 | keyExchange, err := getPrivateKeyExchange(privateKey) 14 | if err != nil { 15 | return nil, err 16 | } 17 | 18 | return &PrivateKeyDecrypter{privateKey: privateKey, keyExchange: keyExchange}, nil 19 | } 20 | 21 | // PrivateKeyDecrypter will decrypt data using NACL with ECDH key exchange 22 | type PrivateKeyDecrypter struct { 23 | privateKey crypto.PrivateKey 24 | keyExchange cipher.KeyExchange 25 | } 26 | 27 | // Decrypt data using recipient private key with AES in CBC mode. 28 | func (d PrivateKeyDecrypter) Decrypt(data cipher.EncryptedContent) (cipher.PlainContent, error) { 29 | data, deserialiseKeyID, err := deserializePrivateKeyEncryptedContent(data) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | privateKeyID, err := multikey.IDFromPrivateKey(d.privateKey) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | if deserialiseKeyID != privateKeyID { 40 | return nil, errors.New("key id does not match") 41 | } 42 | 43 | encryptionKeyBytes, err := encryptionKeyBytes(d.privateKey) 44 | if err != nil { 45 | return nil, err 46 | } 47 | 48 | return easyOpen(data, encryptionKeyBytes) 49 | } 50 | -------------------------------------------------------------------------------- /cipher/nacl/private_key_encrypter.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | "github.com/mailchain/go-crypto/multikey" 10 | ) 11 | 12 | // NewPrivateKeyEncrypter creates a new encrypter with crypto rand for reader, 13 | // and attaching the public key to the encrypter. 14 | func NewPrivateKeyEncrypter(privateKey crypto.PrivateKey) (*PrivateKeyEncrypter, error) { 15 | keyExchange, err := getPrivateKeyExchange(privateKey) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return &PrivateKeyEncrypter{rand: rand.Reader, privateKey: privateKey, keyExchange: keyExchange}, nil 21 | } 22 | 23 | // PrivateKeyEncrypter will encrypt data using AES256CBC method. 24 | type PrivateKeyEncrypter struct { 25 | rand io.Reader 26 | privateKey crypto.PrivateKey 27 | keyExchange cipher.KeyExchange 28 | } 29 | 30 | // Encrypt encrypts the message with the key that was attached to it. 31 | func (e PrivateKeyEncrypter) Encrypt(message cipher.PlainContent) (cipher.EncryptedContent, error) { 32 | encryptionKeyBytes, err := encryptionKeyBytes(e.privateKey) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | encrypted, err := easySeal(message, encryptionKeyBytes, e.rand) 38 | if err != nil { 39 | return nil, err 40 | } 41 | 42 | keyID, err := multikey.IDFromPrivateKey(e.privateKey) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return serializePrivateKeyEncryptedContent(encrypted, keyID), nil 48 | } 49 | -------------------------------------------------------------------------------- /cipher/aes256cbc/ecdh.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "crypto/elliptic" 5 | "crypto/hmac" 6 | "crypto/sha256" 7 | "crypto/sha512" 8 | "errors" 9 | "io" 10 | 11 | "github.com/ethereum/go-ethereum/crypto/ecies" 12 | ) 13 | 14 | // deriveSharedSecret create a shared secret between public and private key. 15 | func deriveSharedSecret(pub *ecies.PublicKey, private *ecies.PrivateKey) ([]byte, error) { 16 | x, _ := pub.ScalarMult(pub.X, pub.Y, private.D.Bytes()) 17 | if x == nil { 18 | return nil, errors.New("failed to derive shared secret") 19 | } 20 | return x.Bytes(), nil 21 | } 22 | 23 | func generateMacKeyAndEncryptionKey(sharedSecret []byte) (macKey, encryptionKey []byte) { 24 | hash := sha512.Sum512(sharedSecret) 25 | encryptionKey = hash[:32] 26 | macKey = hash[32:] 27 | return macKey, encryptionKey 28 | } 29 | 30 | func (e Encrypter) generateIV() ([]byte, error) { 31 | iv := make([]byte, 16) 32 | _, err := io.ReadFull(e.rand, iv) 33 | return iv, err 34 | } 35 | 36 | func generateMac(macKey, iv []byte, ephemeralPublicKey ecies.PublicKey, ciphertext []byte) ([]byte, error) { 37 | // TODO: curve is hard code yet the type is stored in the keystore. Can aes256cbc work with other curves? 38 | pub := elliptic.Marshal(ecies.DefaultCurve, ephemeralPublicKey.X, ephemeralPublicKey.Y) 39 | dataToMac := append(iv, pub...) 40 | dataToMac = append(dataToMac, ciphertext...) 41 | mac := hmac.New(sha256.New, macKey) 42 | _, err := mac.Write(dataToMac) 43 | if err != nil { 44 | return nil, err 45 | } 46 | return mac.Sum(nil), nil 47 | } 48 | -------------------------------------------------------------------------------- /secp256k1/keys_test.go: -------------------------------------------------------------------------------- 1 | package secp256k1 2 | 3 | import ( 4 | "log" 5 | 6 | "github.com/mailchain/go-encoding/encodingtest" 7 | ) 8 | 9 | var ( 10 | alicePrivateKey = func() PrivateKey { 11 | k, err := PrivateKeyFromBytes(alicePrivateKeyBytes) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | return *k 16 | }() //nolint: lll 17 | 18 | alicePrivateKeyBytes = encodingtest.MustDecodeHex("01901E63389EF02EAA7C5782E08B40D98FAEF835F28BD144EECF5614A415943F") 19 | alicePublicKey = func() PublicKey { 20 | k, err := PublicKeyFromBytes(alicePublicKeyBytes) 21 | if err != nil { 22 | log.Fatal(err) 23 | } 24 | 25 | return *k.(*PublicKey) 26 | }() 27 | alicePublicKeyBytes = []byte{0x2, 0x69, 0xd9, 0x8, 0x51, 0xe, 0x35, 0x5b, 0xeb, 0x1d, 0x5b, 0xf2, 0xdf, 0x81, 0x29, 0xe5, 0xb6, 0x40, 0x1e, 0x19, 0x69, 0x89, 0x1e, 0x80, 0x16, 0xa0, 0xb2, 0x30, 0x7, 0x39, 0xbb, 0xb0, 0x6} 28 | ) 29 | 30 | var ( 31 | bobPrivateKey = func() PrivateKey { 32 | k, err := PrivateKeyFromBytes(bobPrivateKeyBytes) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | return *k 37 | }() //nolint: lll 38 | 39 | bobPrivateKeyBytes = encodingtest.MustDecodeHex("DF4BA9F6106AD2846472F759476535E55C5805D8337DF5A11C3B139F438B98B3") 40 | bobPublicKey = func() PublicKey { 41 | k, err := PublicKeyFromBytes(bobPublicKeyBytes) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | return *k.(*PublicKey) 46 | }() 47 | bobPublicKeyBytes = []byte{0x3, 0xbd, 0xf6, 0xfb, 0x97, 0xc9, 0x7c, 0x12, 0x6b, 0x49, 0x21, 0x86, 0xa4, 0xd5, 0xb2, 0x8f, 0x34, 0xf0, 0x67, 0x1a, 0x5a, 0xac, 0xc9, 0x74, 0xda, 0x3b, 0xde, 0xb, 0xe9, 0x3e, 0x45, 0xa1, 0xc5} 48 | ) 49 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Mailchain - Crypto 2 | 3 | ## Mailchain 4 | 5 | Mailchain is a multi-chain communication protocol that helps you communicate with your users across any protocol. It provides end-to-end encryption by default, and supports 1:1, 1:many and group messaging. 6 | 7 | Using Mailchain you can easily send messages to any blockchain address on different protocols. 8 | 9 | ## Crypto 10 | 11 | Mailchain supports messaging across multiple chains. Each blockchain protocol uses a predefined elliptic curve usage. Mailchain needs to support signatures and encryption for all blockchain protocols. This repository contains Mailchain crypto functionality. 12 | 13 | ## Find out more 14 | 15 | We'd :heart: for you to join our growing community and get involved. 16 | 17 | Follow [@mailchain_xyz](https://twitter.com/mailchain_xyz) on Twitter to hear about new releases and updates. 18 | 19 | Other channels to reach us can be found here: [https://docs.mailchain.com/getting-help/](https://docs.mailchain.com/getting-help/). 20 | 21 | ## License 22 | 23 | Copyright 2022, Mailchain Ltd. All rights reserved. 24 | 25 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 26 | these files except in compliance with the License. You may obtain a copy of the 27 | License at 28 | 29 | http://www.apache.org/licenses/LICENSE-2.0 30 | 31 | Unless required by applicable law or agreed to in writing, software distributed 32 | under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 33 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 34 | specific language governing permissions and limitations under the License. 35 | -------------------------------------------------------------------------------- /ed25519/private.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | 8 | "github.com/mailchain/go-crypto" 9 | "golang.org/x/crypto/ed25519" 10 | ) 11 | 12 | // PrivateKey based on the secp256k1 curve 13 | type PrivateKey struct { 14 | Key ed25519.PrivateKey 15 | } 16 | 17 | // Bytes returns the byte representation of the private key 18 | func (pk PrivateKey) Bytes() []byte { 19 | return append(pk.Key.Seed(), pk.PublicKey().Bytes()...) 20 | } 21 | 22 | // Sign signs the message with the private key and returns the signature. 23 | func (pk PrivateKey) Sign(message []byte) (signature []byte, err error) { 24 | if len(pk.Key) != ed25519.PrivateKeySize { 25 | return nil, errors.New("invalid key length") 26 | } 27 | 28 | return ed25519.Sign(pk.Key, message), nil 29 | } 30 | 31 | // PublicKey return the public key that is derived from the private key 32 | func (pk PrivateKey) PublicKey() crypto.PublicKey { 33 | publicKey := make([]byte, ed25519.PublicKeySize) 34 | copy(publicKey, pk.Key[32:]) 35 | 36 | return &PublicKey{Key: publicKey} 37 | } 38 | 39 | // PrivateKeyFromBytes get a private key from seed []byte 40 | func PrivateKeyFromBytes(privKey []byte) (*PrivateKey, error) { 41 | switch len(privKey) { 42 | case ed25519.SeedSize: 43 | return &PrivateKey{Key: ed25519.NewKeyFromSeed(privKey)}, nil 44 | case ed25519.PrivateKeySize: 45 | return &PrivateKey{Key: privKey}, nil 46 | default: 47 | return nil, fmt.Errorf("ed25519: bad key length") 48 | } 49 | } 50 | 51 | func GenerateKey(rand io.Reader) (*PrivateKey, error) { 52 | _, pPrivKey, err := ed25519.GenerateKey(rand) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return PrivateKeyFromBytes(pPrivKey) 58 | } 59 | -------------------------------------------------------------------------------- /secp256k1/secp256k1test/keys.go: -------------------------------------------------------------------------------- 1 | // Package secp256k1test contains keys that are used to ease testing of Mailchain 2 | // functionality. 3 | // All keys in this package are publicly known and therefore compromised. Keys 4 | // MUST not be used on any live networks, as secrets, or for any purpose other 5 | // than creating a reproducible unsecured test. 6 | package secp256k1test 7 | 8 | import ( 9 | "log" 10 | 11 | "github.com/mailchain/go-crypto" 12 | "github.com/mailchain/go-crypto/secp256k1" 13 | "github.com/mailchain/go-encoding/encodingtest" 14 | ) 15 | 16 | // AlicePrivateKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 17 | var AlicePrivateKey crypto.PrivateKey //nolint: gochecknoglobals 18 | // AlicePublicKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 19 | var AlicePublicKey crypto.PublicKey //nolint: gochecknoglobals 20 | // BobPrivateKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 21 | var BobPrivateKey crypto.PrivateKey //nolint: gochecknoglobals 22 | // BobPublicKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 23 | var BobPublicKey crypto.PublicKey //nolint: gochecknoglobals 24 | 25 | // nolint: gochecknoinits 26 | func init() { 27 | var err error 28 | 29 | AlicePrivateKey, err = secp256k1.PrivateKeyFromBytes(encodingtest.MustDecodeHex("01901E63389EF02EAA7C5782E08B40D98FAEF835F28BD144EECF5614A415943F")) 30 | if err != nil { 31 | log.Fatal(err) 32 | } 33 | 34 | AlicePublicKey = AlicePrivateKey.PublicKey() 35 | 36 | BobPrivateKey, err = secp256k1.PrivateKeyFromBytes(encodingtest.MustDecodeHex("DF4BA9F6106AD2846472F759476535E55C5805D8337DF5A11C3B139F438B98B3")) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | BobPublicKey = BobPrivateKey.PublicKey() 42 | } 43 | -------------------------------------------------------------------------------- /multikey/private_test.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | "github.com/mailchain/go-encoding/encodingtest" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestPrivateKeyFromBytes(t *testing.T) { 15 | type args struct { 16 | hex string 17 | keyType []byte 18 | } 19 | tests := []struct { 20 | name string 21 | args args 22 | want crypto.PrivateKey 23 | wantErr bool 24 | }{ 25 | { 26 | "secp256k1", 27 | args{ 28 | "secp256k1", 29 | secp256k1test.AlicePrivateKey.Bytes(), 30 | }, 31 | secp256k1test.AlicePrivateKey, 32 | false, 33 | }, 34 | { 35 | "ed25519", 36 | args{ 37 | "ed25519", 38 | ed25519test.AlicePrivateKey.Bytes(), 39 | }, 40 | ed25519test.AlicePrivateKey, 41 | false, 42 | }, 43 | { 44 | "sr25519-Bob", 45 | args{ 46 | "sr25519", 47 | encodingtest.MustDecodeHex("23b063a581fd8e5e847c4e2b9c494247298791530f5293be369e8bf23a45d2bd"), 48 | }, 49 | sr25519test.BobPrivateKey, 50 | false, 51 | }, 52 | { 53 | "err", 54 | args{ 55 | "unknown", 56 | secp256k1test.AlicePrivateKey.Bytes(), 57 | }, 58 | nil, 59 | true, 60 | }, 61 | } 62 | for _, tt := range tests { 63 | t.Run(tt.name, func(t *testing.T) { 64 | got, err := PrivateKeyFromBytes(tt.args.hex, tt.args.keyType) 65 | if (err != nil) != tt.wantErr { 66 | t.Errorf("PrivateKeyFromBytes() error = %v, wantErr %v", err, tt.wantErr) 67 | return 68 | } 69 | if !assert.Equal(t, tt.want, got) { 70 | t.Errorf("PrivateKeyFromBytes() = %v, want %v", got, tt.want) 71 | } 72 | }) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /cipher/noop/decrypter_test.go: -------------------------------------------------------------------------------- 1 | package noop 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/mailchain/go-crypto/cipher" 9 | ) 10 | 11 | func TestNewDecrypter(t *testing.T) { 12 | tests := []struct { 13 | name string 14 | want Decrypter 15 | }{ 16 | { 17 | "success", 18 | Decrypter{}, 19 | }, 20 | } 21 | for _, tt := range tests { 22 | t.Run(tt.name, func(t *testing.T) { 23 | if got := NewDecrypter(); !reflect.DeepEqual(got, tt.want) { 24 | t.Errorf("NewDecrypter() = %v, want %v", got, tt.want) 25 | } 26 | }) 27 | } 28 | } 29 | 30 | func TestDecrypter_Decrypt(t *testing.T) { 31 | type args struct { 32 | data cipher.EncryptedContent 33 | } 34 | tests := []struct { 35 | name string 36 | d Decrypter 37 | args args 38 | want cipher.PlainContent 39 | err error 40 | wantErr bool 41 | }{ 42 | { 43 | "success", 44 | NewDecrypter(), 45 | args{ 46 | bytesEncode(cipher.EncryptedContent([]byte("test content"))), 47 | }, 48 | cipher.PlainContent([]byte("test content")), 49 | nil, 50 | false, 51 | }, 52 | { 53 | "err-invalid-prefix", 54 | NewDecrypter(), 55 | args{ 56 | cipher.EncryptedContent([]byte("test content")), 57 | }, 58 | nil, 59 | fmt.Errorf("invalid prefix"), 60 | true, 61 | }, 62 | } 63 | for _, tt := range tests { 64 | t.Run(tt.name, func(t *testing.T) { 65 | d := Decrypter{} 66 | got, err := d.Decrypt(tt.args.data) 67 | if (err != nil) != tt.wantErr { 68 | t.Errorf("Decrypter.Decrypt() error = %v, wantErr %v", err, tt.wantErr) 69 | return 70 | } else if err != nil && err.Error() != tt.err.Error() { 71 | t.Errorf("Decrypter.Decrypt() error = %v, want %v", err, tt.err) 72 | } 73 | if !reflect.DeepEqual(got, tt.want) { 74 | t.Errorf("Decrypter.Decrypt() = %v, want %v", got, tt.want) 75 | } 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cipher/noop/encrypter_test.go: -------------------------------------------------------------------------------- 1 | package noop 2 | 3 | import ( 4 | "crypto" 5 | "reflect" 6 | "testing" 7 | 8 | keys "github.com/mailchain/go-crypto" 9 | "github.com/mailchain/go-crypto/cipher" 10 | ) 11 | 12 | func TestNewEncrypter(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | want cipher.Encrypter 16 | wantErr bool 17 | }{ 18 | { 19 | "success", 20 | &Encrypter{}, 21 | false, 22 | }, 23 | } 24 | for _, tt := range tests { 25 | t.Run(tt.name, func(t *testing.T) { 26 | got, err := NewEncrypter(nil) 27 | if (err != nil) != tt.wantErr { 28 | t.Errorf("Encrypter.Encrypt() error = %v, wantErr %v", err, tt.wantErr) 29 | } 30 | 31 | if !reflect.DeepEqual(got, tt.want) { 32 | t.Errorf("NewEncrypter() = %v, want %v", got, tt.want) 33 | } 34 | }) 35 | } 36 | } 37 | 38 | func TestEncrypter_Encrypt(t *testing.T) { 39 | type fields struct { 40 | publicKey keys.PublicKey 41 | } 42 | type args struct { 43 | recipientPublicKey crypto.PublicKey 44 | message cipher.PlainContent 45 | } 46 | tests := []struct { 47 | name string 48 | fields fields 49 | args args 50 | want cipher.EncryptedContent 51 | wantErr bool 52 | }{ 53 | { 54 | "success", 55 | fields{ 56 | nil, 57 | }, 58 | args{ 59 | nil, 60 | cipher.PlainContent([]byte("test content")), 61 | }, 62 | bytesEncode(cipher.EncryptedContent([]byte("test content"))), 63 | false, 64 | }, 65 | } 66 | for _, tt := range tests { 67 | t.Run(tt.name, func(t *testing.T) { 68 | e := Encrypter{publicKey: tt.fields.publicKey} 69 | got, err := e.Encrypt(tt.args.message) 70 | if (err != nil) != tt.wantErr { 71 | t.Errorf("Encrypter.Encrypt() error = %v, wantErr %v", err, tt.wantErr) 72 | return 73 | } 74 | if !reflect.DeepEqual(got, tt.want) { 75 | t.Errorf("Encrypter.Encrypt() = %v, want %v", got, tt.want) 76 | } 77 | }) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /secp256k1/public.go: -------------------------------------------------------------------------------- 1 | package secp256k1 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "fmt" 6 | 7 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 8 | "github.com/ethereum/go-ethereum/crypto/ecies" 9 | "github.com/mailchain/go-crypto" 10 | ) 11 | 12 | // PublicKey based on the secp256k1 curve 13 | type PublicKey struct { 14 | ecdsa ecdsa.PublicKey 15 | } 16 | 17 | // Verify verifies whether sig is a valid signature of message. 18 | func (pk PublicKey) Verify(message, sig []byte) bool { 19 | if len(sig) == 65 { 20 | sig = sig[:64] 21 | } 22 | return ethcrypto.VerifySignature(ethcrypto.CompressPubkey(&pk.ecdsa), message[:], sig) 23 | } 24 | 25 | // Bytes returns the byte representation of the public key 26 | func (pk PublicKey) Bytes() []byte { 27 | return ethcrypto.CompressPubkey(&pk.ecdsa) 28 | } 29 | 30 | func (pk PublicKey) UncompressedBytes() []byte { 31 | return append(pk.ecdsa.X.Bytes(), pk.ecdsa.Y.Bytes()...) 32 | } 33 | 34 | // PublicKeyFromBytes create a public key from []byte 35 | func PublicKeyFromBytes(keyBytes []byte) (crypto.PublicKey, error) { 36 | switch len(keyBytes) { 37 | case 65: 38 | rpk, err := ethcrypto.UnmarshalPubkey(keyBytes) 39 | if err != nil { 40 | return nil, fmt.Errorf("could not convert pk: %w", err) 41 | } 42 | 43 | return &PublicKey{ecdsa: *rpk}, nil 44 | case 64: 45 | rpk, err := ethcrypto.UnmarshalPubkey(append([]byte{byte(4)}, keyBytes...)) 46 | if err != nil { 47 | return nil, fmt.Errorf("could not convert pk: %w", err) 48 | } 49 | 50 | return &PublicKey{ecdsa: *rpk}, nil 51 | case 33: 52 | pk, err := ethcrypto.DecompressPubkey(keyBytes) 53 | if err != nil { 54 | return nil, fmt.Errorf("could not decompress pk: %w", err) 55 | } 56 | 57 | return &PublicKey{ecdsa: *pk}, nil 58 | default: 59 | return nil, fmt.Errorf("invalid key length %v", len(keyBytes)) 60 | } 61 | } 62 | 63 | // ECIES returns an ECIES representation of the public key. 64 | func (pk PublicKey) ECIES() (*ecies.PublicKey, error) { 65 | return ecies.ImportECDSAPublic(&pk.ecdsa), nil 66 | } 67 | 68 | func (pk PublicKey) ECDSA() *ecdsa.PublicKey { 69 | return &pk.ecdsa 70 | } 71 | -------------------------------------------------------------------------------- /cipher/cipher.go: -------------------------------------------------------------------------------- 1 | // Package cipher collects common cryptographic constants and interfaces. 2 | package cipher 3 | 4 | //go:generate mockgen -source=cipher.go -package=ciphertest -destination=./ciphertest/cipher_mock.go 5 | 6 | const ( 7 | // NoOperation identified for Encrypt and Decrypter in noop package. 8 | NoOperation byte = 0x20 9 | 10 | // NACLECDH identified for Encrypt and Decrypter in nacl package using ECDH 11 | // for key exchange and share secret generation. 12 | NACLECDH byte = 0x2a 13 | 14 | // NACLSecretKey indenified for nacl secret key encryption. 15 | NACLSecretKey byte = 0x2b 16 | 17 | // AES256CBC identified for Encrypt and Decrypter in aes256cbc package. 18 | AES256CBC byte = 0x2e 19 | ) 20 | 21 | // EncryptedContent typed version of byte array that holds encrypted data. 22 | // 23 | // Encrypt method returns the encrypted contents as EncryptedContent. 24 | // Decrypt method accepts EncryptedContent as the encrypted contents to decrypt. 25 | type EncryptedContent []byte 26 | 27 | // PlainContent typed version of byte array that holds plain data. 28 | // 29 | // Encrypt method returns the encrypted contents as EncryptedContent. 30 | // Decrypt method accepts EncryptedContent as the encrypted contents to decrypt. 31 | type PlainContent []byte 32 | 33 | // A Decrypter uses the PrivateKey to decrypt the supplied data. 34 | // 35 | // The decryption method used is dependant on the implementation and 36 | // must check that the data can be decrypted before continuing. 37 | // Returned data should be the plain bytes that were supplied 38 | // originally to the Encrypter. 39 | type Decrypter interface { 40 | Decrypt(EncryptedContent) (PlainContent, error) 41 | } 42 | 43 | // An Encrypter uses the PublicKey to encrypt the supplied data. 44 | // 45 | // The encryption method used is dependant on the implementation and must be included in the response. 46 | // Returned encrypted data must include what encryption method was used as the first byte. 47 | // The data can be decrypted using the corresponding PrivateKey and Decrypter method. 48 | type Encrypter interface { 49 | Encrypt(PlainContent) (EncryptedContent, error) 50 | } 51 | -------------------------------------------------------------------------------- /sr25519/public.go: -------------------------------------------------------------------------------- 1 | package sr25519 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/gtank/ristretto255" 7 | "github.com/mailchain/go-crypto" 8 | ) 9 | 10 | const ( 11 | publicKeySize = 32 12 | ) 13 | 14 | // PublicKey based on the sr25519 curve 15 | type PublicKey struct { 16 | key []byte 17 | } 18 | 19 | // Bytes return Publickey Bytes 20 | func (pk PublicKey) Bytes() []byte { 21 | return pk.key 22 | } 23 | 24 | // Verify uses the sr25519 signature algorithm to verify that the message was signed by 25 | // this public key; it returns true if this key created the signature for the message, 26 | // false otherwise 27 | func (pk PublicKey) Verify(message, sig []byte) bool { 28 | signature := signature{} 29 | if err := signature.Decode(sig); err != nil { 30 | return false 31 | } 32 | 33 | context := newSigningContext(substrateContext, message) 34 | context.AppendMessage([]byte("proto-name"), []byte("Schnorr-sig")) 35 | context.AppendMessage([]byte("sign:pk"), pk.key) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L212 36 | context.AppendMessage([]byte("sign:R"), signature.R.Encode([]byte{})) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L213 37 | 38 | k := context.challengeScalar([]byte("sign:c")) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L215 39 | // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L216 40 | a := ristretto255.NewElement() 41 | if err := a.Decode(pk.key); err != nil { 42 | return false 43 | } 44 | 45 | Rp := ristretto255.NewElement() 46 | Rp = Rp.ScalarBaseMult(signature.S) 47 | ky := a.ScalarMult(k, a) 48 | Rp = Rp.Subtract(Rp, ky) 49 | 50 | return Rp.Equal(signature.R) == 1 51 | } 52 | 53 | // PublicKeyFromBytes - Convert byte array to PublicKey 54 | func PublicKeyFromBytes(keyBytes []byte) (crypto.PublicKey, error) { 55 | switch len(keyBytes) { 56 | case publicKeySize: 57 | return &PublicKey{key: keyBytes}, nil 58 | default: 59 | return nil, errors.New("public key must be 32 bytes") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /sr25519/sign.go: -------------------------------------------------------------------------------- 1 | package sr25519 2 | 3 | import ( 4 | "crypto/rand" 5 | "errors" 6 | "io" 7 | 8 | "github.com/gtank/merlin" 9 | "github.com/gtank/ristretto255" 10 | ) 11 | 12 | var substrateContext = []byte("substrate") //nolint gochecknoglobals 13 | const ( 14 | signatureSize = 64 15 | ) 16 | 17 | type signingContext struct { 18 | *merlin.Transcript 19 | } 20 | 21 | func newSigningContext(context, msg []byte) signingContext { 22 | transcript := merlin.NewTranscript("SigningContext") 23 | transcript.AppendMessage([]byte(""), context) 24 | transcript.AppendMessage([]byte("sign-bytes"), msg) 25 | 26 | return signingContext{transcript} 27 | } 28 | 29 | func (c *signingContext) challengeScalar(label []byte) *ristretto255.Scalar { 30 | b := c.ExtractBytes(label, signatureSize) 31 | k := ristretto255.NewScalar() 32 | 33 | return k.FromUniformBytes(b) 34 | } 35 | 36 | func witness(nonce []byte) (*ristretto255.Scalar, error) { 37 | _ = nonce 38 | b := make([]byte, signatureSize) 39 | 40 | _, err := io.ReadFull(rand.Reader, b) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | return ristretto255.NewScalar().FromUniformBytes(b), nil 46 | } 47 | 48 | type signature struct { 49 | R *ristretto255.Element 50 | S *ristretto255.Scalar 51 | } 52 | 53 | func (s *signature) Encode() []byte { // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L91 54 | out := make([]byte, signatureSize) 55 | copy(out[:32], s.R.Encode([]byte{})) 56 | copy(out[32:], s.S.Encode([]byte{})) 57 | out[63] |= 128 58 | 59 | return out 60 | } 61 | 62 | func (s *signature) Decode(sig []byte) error { // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L114 63 | if len(sig) != signatureSize { 64 | return errors.New("signature length must be 64") 65 | } 66 | 67 | s.R = ristretto255.NewElement() 68 | if err := s.R.Decode(sig[:32]); err != nil { 69 | return err 70 | } 71 | 72 | if sig[63]&128 == 0 { 73 | return errors.New("signature not marked as schnorrkel") 74 | } 75 | 76 | sig[63] &= 127 77 | s.S = ristretto255.NewScalar() 78 | 79 | return s.S.Decode(sig[32:]) 80 | } 81 | -------------------------------------------------------------------------------- /cipher/ecdh/sr25519.go: -------------------------------------------------------------------------------- 1 | package ecdh 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | 8 | "github.com/mailchain/go-crypto" 9 | "github.com/mailchain/go-crypto/sr25519" 10 | ) 11 | 12 | type SR25519 struct { 13 | rand io.Reader 14 | } 15 | 16 | func NewSR25519(rand io.Reader) (*SR25519, error) { 17 | if rand == nil { 18 | return nil, errors.New("rand must not be nil") 19 | } 20 | 21 | return &SR25519{rand: rand}, nil 22 | } 23 | 24 | func (kx SR25519) EphemeralKey() (crypto.PrivateKey, error) { 25 | return sr25519.GenerateKey(kx.rand) 26 | } 27 | 28 | // SharedSecret computes a secret value from a private / public key pair. 29 | // On sending a message the private key should be an ephemeralKey or generated private key, 30 | // the public key is the recipient public key. 31 | // On reading a message the private key is the recipient private key, the public key is the 32 | // ephemeralKey or generated public key. 33 | func (kx SR25519) SharedSecret(privateKey crypto.PrivateKey, publicKey crypto.PublicKey) ([]byte, error) { 34 | sr25519PrivateKey, err := kx.privateKey(privateKey) 35 | if err != nil { 36 | return nil, ErrSharedSecretGenerate 37 | } 38 | 39 | sr25519PublicKey, err := kx.publicKey(publicKey) 40 | if err != nil { 41 | return nil, ErrSharedSecretGenerate 42 | } 43 | 44 | ephemeralPublicKey, _ := kx.publicKey(privateKey.PublicKey()) 45 | 46 | if bytes.Equal(ephemeralPublicKey.Bytes(), sr25519PublicKey.Bytes()) { 47 | return nil, ErrSharedSecretGenerate 48 | } 49 | 50 | sharedSecret, err := sr25519.ExchangeKeys(sr25519PrivateKey, sr25519PublicKey, 32) 51 | if err != nil { 52 | return nil, ErrSharedSecretGenerate 53 | } 54 | 55 | return sharedSecret, nil 56 | } 57 | 58 | func (kx SR25519) publicKey(pubKey crypto.PublicKey) (*sr25519.PublicKey, error) { 59 | switch pk := pubKey.(type) { 60 | case *sr25519.PublicKey: 61 | return pk, nil 62 | default: 63 | return nil, ErrSharedSecretGenerate 64 | } 65 | } 66 | 67 | func (kx SR25519) privateKey(privKey crypto.PrivateKey) (*sr25519.PrivateKey, error) { 68 | switch pk := privKey.(type) { 69 | case *sr25519.PrivateKey: 70 | return pk, nil 71 | default: 72 | return nil, ErrSharedSecretGenerate 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /multikey/kind.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | 7 | "github.com/mailchain/go-crypto" 8 | ) 9 | 10 | var ( 11 | // ErrInconclusive is returned when multiple public keys matches for the same input. 12 | ErrInconclusive = errors.New("multiple matches found") 13 | 14 | // ErrNoMatch is returned when no public key matches for the input. 15 | ErrNoMatch = errors.New("no match found") 16 | 17 | // errPrivateKeyPublicKeyNotMatched private and public keys do not match 18 | errPrivateAndPublicKeyNotMatched = errors.New("public and private keys do not match") 19 | ) 20 | 21 | const ( 22 | noKeyMatch = 0 23 | singleKeyMatch = 1 24 | ) 25 | 26 | // GetKeyKindFromBytes extracts the private key type from the publicKey and privateKey. 27 | // Supported private key types are defined in PossibleKeyKinds variable. 28 | func GetKeyKindFromBytes(publicKey, privateKey []byte) (crypto.PrivateKey, error) { 29 | matches := make([]crypto.PrivateKey, 0, 1) 30 | 31 | for keyKind := range crypto.KeyTypes() { 32 | cPrivateKey, err := extractKeyTypeAndVerifyPrivateAndPublicKey(publicKey, privateKey, keyKind) 33 | if err != nil { 34 | continue 35 | } 36 | 37 | matches = append(matches, cPrivateKey) 38 | } 39 | 40 | switch len(matches) { 41 | case noKeyMatch: 42 | return nil, ErrNoMatch 43 | case singleKeyMatch: 44 | return matches[0], nil 45 | default: 46 | return nil, ErrInconclusive 47 | } 48 | } 49 | 50 | func extractKeyTypeAndVerifyPrivateAndPublicKey(publicKey, privateKey []byte, kind string) (crypto.PrivateKey, error) { 51 | cPrivateKey, err := PrivateKeyFromBytes(kind, privateKey) 52 | if err != nil { 53 | return nil, err 54 | } 55 | 56 | if bytes.Equal(cPrivateKey.PublicKey().Bytes(), publicKey) { 57 | return cPrivateKey, nil 58 | } 59 | 60 | return nil, errPrivateAndPublicKeyNotMatched 61 | } 62 | 63 | func removeDuplicates(x []string) []string { 64 | if x == nil { 65 | return nil 66 | } 67 | 68 | set := make(map[string]struct{}) 69 | unique := []string{} 70 | 71 | for _, str := range x { 72 | if _, ok := set[str]; !ok { 73 | set[str] = struct{}{} 74 | 75 | unique = append(unique, str) 76 | } 77 | } 78 | 79 | return unique 80 | } 81 | -------------------------------------------------------------------------------- /multikey/public.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/ed25519" 9 | "github.com/mailchain/go-crypto/secp256k1" 10 | "github.com/mailchain/go-crypto/secp256r1" 11 | "github.com/mailchain/go-crypto/sr25519" 12 | "github.com/mailchain/go-encoding" 13 | ) 14 | 15 | // PublicKeyFromBytes use the correct function to get the private key from bytes 16 | func PublicKeyFromBytes(keyType string, data []byte) (crypto.PublicKey, error) { 17 | switch keyType { 18 | case crypto.KindSECP256K1: 19 | return secp256k1.PublicKeyFromBytes(data) 20 | case crypto.KindED25519: 21 | return ed25519.PublicKeyFromBytes(data) 22 | case crypto.KindSR25519: 23 | return sr25519.PublicKeyFromBytes(data) 24 | case crypto.KindSECP256R1: 25 | return secp256r1.PublicKeyFromBytes(data) 26 | default: 27 | return nil, fmt.Errorf("unsupported curve type") 28 | } 29 | } 30 | 31 | func DescriptivePublicKeyFromEncodedString(in string, encodedWith string) (crypto.PublicKey, error) { 32 | decodedBytes, err := encoding.Decode(encodedWith, in) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | return DescriptivePublicKeyFromBytes(decodedBytes) 38 | } 39 | 40 | func DescriptivePublicKeyFromBytes(in []byte) (crypto.PublicKey, error) { 41 | if len(in) <= 1 { 42 | return nil, errors.New("input must contain id and public key") 43 | } 44 | 45 | keyType := in[0] 46 | data := in[1:] // skip the id byte and return rest 47 | 48 | switch keyType { 49 | case crypto.IDSECP256K1: 50 | return secp256k1.PublicKeyFromBytes(data) 51 | case crypto.IDED25519: 52 | return ed25519.PublicKeyFromBytes(data) 53 | case crypto.IDSR25519: 54 | return sr25519.PublicKeyFromBytes(data) 55 | case crypto.IDSECP256R1: 56 | return secp256r1.PublicKeyFromBytes(data) 57 | default: 58 | return nil, fmt.Errorf("first byte must identity key curve") 59 | } 60 | } 61 | 62 | func DescriptiveBytesFromPublicKey(in crypto.PublicKey) ([]byte, error) { 63 | idByte, err := IDFromPublicKey(in) 64 | if err != nil { 65 | return nil, err 66 | } 67 | 68 | out := make([]byte, len(in.Bytes())+1) 69 | out[0] = idByte 70 | copy(out[1:], in.Bytes()) 71 | 72 | return out, nil 73 | } 74 | -------------------------------------------------------------------------------- /cipher/encrypter/encrypter_test.go: -------------------------------------------------------------------------------- 1 | package encrypter 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/cipher" 8 | "github.com/mailchain/go-crypto/cipher/aes256cbc" 9 | "github.com/mailchain/go-crypto/cipher/nacl" 10 | "github.com/mailchain/go-crypto/cipher/noop" 11 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestGetEncrypter(t *testing.T) { 16 | type args struct { 17 | encryption string 18 | pubKey crypto.PublicKey 19 | } 20 | tests := []struct { 21 | name string 22 | args args 23 | want cipher.Encrypter 24 | wantErr bool 25 | }{ 26 | { 27 | "aes256cbc", 28 | args{ 29 | "aes256cbc", 30 | secp256k1test.AlicePublicKey, 31 | }, 32 | func() cipher.Encrypter { 33 | encrypter, _ := aes256cbc.NewEncrypter(secp256k1test.AlicePublicKey) 34 | return encrypter 35 | }(), 36 | false, 37 | }, 38 | { 39 | "nacl-ecdh", 40 | args{ 41 | "nacl-ecdh", 42 | secp256k1test.AlicePublicKey, 43 | }, 44 | func() cipher.Encrypter { 45 | encrypter, _ := nacl.NewPublicKeyEncrypter(secp256k1test.AlicePublicKey) 46 | return encrypter 47 | }(), 48 | false, 49 | }, 50 | { 51 | "noop", 52 | args{ 53 | "noop", 54 | secp256k1test.AlicePublicKey, 55 | }, 56 | func() cipher.Encrypter { 57 | encrypter, _ := noop.NewEncrypter(secp256k1test.AlicePublicKey) 58 | return encrypter 59 | }(), 60 | false, 61 | }, 62 | { 63 | "err-empty", 64 | args{ 65 | "", 66 | secp256k1test.AlicePublicKey, 67 | }, 68 | nil, 69 | true, 70 | }, 71 | { 72 | "err-invalid", 73 | args{ 74 | "invalid", 75 | secp256k1test.AlicePublicKey, 76 | }, 77 | nil, 78 | true, 79 | }, 80 | } 81 | for _, tt := range tests { 82 | t.Run(tt.name, func(t *testing.T) { 83 | got, err := GetEncrypter(tt.args.encryption, tt.args.pubKey) 84 | if (err != nil) != tt.wantErr { 85 | t.Errorf("GetEncrypter() error = %v, wantErr %v", err, tt.wantErr) 86 | return 87 | } 88 | if !assert.Equal(t, tt.want, got) { 89 | t.Errorf("GetEncrypter() = %v, want %v", got, tt.want) 90 | } 91 | }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /sr25519/keys_test.go: -------------------------------------------------------------------------------- 1 | package sr25519 2 | 3 | import ( 4 | "github.com/mailchain/go-crypto/internal/schnorrkel" 5 | "github.com/mailchain/go-encoding/encodingtest" 6 | ) 7 | 8 | var ( //nolint 9 | aliceSeed = encodingtest.MustDecodeHex("5c6d7adf75bda1180c225d25f3aa8dc174bbfb3cddee11ae9a85982f6faf791a") //nolint: lll 10 | alicePrivateKey = PrivateKey{secretKey: schnorrkel.NewSecretKeySR25519(func() [32]byte { 11 | s := [32]byte{} 12 | copy(s[:], aliceSeed) 13 | return s 14 | }())} 15 | alicePrivateKeyBytes = []byte{0x5c, 0x6d, 0x7a, 0xdf, 0x75, 0xbd, 0xa1, 0x18, 0xc, 0x22, 0x5d, 0x25, 0xf3, 0xaa, 0x8d, 0xc1, 0x74, 0xbb, 0xfb, 0x3c, 0xdd, 0xee, 0x11, 0xae, 0x9a, 0x85, 0x98, 0x2f, 0x6f, 0xaf, 0x79, 0x1a} //nolint: lll 16 | alicePublicKey = PublicKey{key: alicePublicKeyBytes} //nolint: lll 17 | alicePublicKeyBytes = []byte{0x16, 0x9a, 0x11, 0x72, 0x18, 0x51, 0xf5, 0xdf, 0xf3, 0x54, 0x1d, 0xd5, 0xc4, 0xb0, 0xb4, 0x78, 0xac, 0x1c, 0xd0, 0x92, 0xc9, 0xd5, 0x97, 0x6e, 0x83, 0xda, 0xa0, 0xd0, 0x3f, 0x26, 0x62, 0xc} //nolint: lll 18 | bobSeed = encodingtest.MustDecodeHex("23b063a581fd8e5e847c4e2b9c494247298791530f5293be369e8bf23a45d2bd") //nolint: lll 19 | bobPrivateKey = PrivateKey{secretKey: schnorrkel.NewSecretKeySR25519(func() [32]byte { 20 | s := [32]byte{} 21 | copy(s[:], bobSeed) 22 | return s 23 | }())} 24 | bobPrivateKeyBytes = []byte{0x23, 0xb0, 0x63, 0xa5, 0x81, 0xfd, 0x8e, 0x5e, 0x84, 0x7c, 0x4e, 0x2b, 0x9c, 0x49, 0x42, 0x47, 0x29, 0x87, 0x91, 0x53, 0xf, 0x52, 0x93, 0xbe, 0x36, 0x9e, 0x8b, 0xf2, 0x3a, 0x45, 0xd2, 0xbd} //nolint: lll 25 | bobPublicKey = PublicKey{key: bobPublicKeyBytes} //nolint: lll 26 | bobPublicKeyBytes = []byte{0x84, 0x62, 0x3e, 0x72, 0x52, 0xe4, 0x11, 0x38, 0xaf, 0x69, 0x4, 0xe1, 0xb0, 0x23, 0x4, 0xc9, 0x41, 0x62, 0x5f, 0x39, 0xe5, 0x76, 0x25, 0x89, 0x12, 0x5d, 0xc1, 0xa2, 0xf2, 0xcf, 0x2e, 0x30} //nolint: lll 27 | ) //nolint: lll 28 | -------------------------------------------------------------------------------- /cipher/ecdh/secp256k1.go: -------------------------------------------------------------------------------- 1 | package ecdh 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "errors" 7 | "io" 8 | 9 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 10 | "github.com/mailchain/go-crypto" 11 | "github.com/mailchain/go-crypto/secp256k1" 12 | ) 13 | 14 | type SECP256K1 struct { 15 | rand io.Reader 16 | curve elliptic.Curve 17 | } 18 | 19 | func NewSECP256K1(rand io.Reader) (*SECP256K1, error) { 20 | if rand == nil { 21 | return nil, errors.New("rand must not be nil") 22 | } 23 | 24 | return &SECP256K1{rand: rand, curve: ethcrypto.S256()}, nil 25 | } 26 | 27 | func (kx SECP256K1) EphemeralKey() (crypto.PrivateKey, error) { 28 | return secp256k1.GenerateKey(kx.rand) 29 | } 30 | 31 | // SharedSecret computes a secret value from a private / public key pair. 32 | // On sending a message the private key should be an ephemeralKey or generated private key, 33 | // the public key is the recipient public key. 34 | // On reading a message the private key is the recipient private key, the public key is the 35 | // ephemeralKey or generated public key. 36 | func (kx SECP256K1) SharedSecret(privateKey crypto.PrivateKey, publicKey crypto.PublicKey) ([]byte, error) { 37 | secp256k1PrivateKey, err := kx.privateKey(privateKey) 38 | if err != nil { 39 | return nil, ErrSharedSecretGenerate 40 | } 41 | 42 | secp256k1PublicKey, err := kx.publicKey(publicKey) 43 | if err != nil { 44 | return nil, ErrSharedSecretGenerate 45 | } 46 | 47 | ephemeralPublicKey, _ := kx.publicKey(privateKey.PublicKey()) 48 | if ephemeralPublicKey.X == secp256k1PublicKey.X && ephemeralPublicKey.Y == secp256k1PublicKey.Y { 49 | return nil, ErrSharedSecretGenerate 50 | } 51 | 52 | sX, _ := kx.curve.ScalarMult(secp256k1PublicKey.X, secp256k1PublicKey.Y, secp256k1PrivateKey.D.Bytes()) 53 | 54 | return sX.Bytes(), nil 55 | } 56 | 57 | func (kx SECP256K1) publicKey(pubKey crypto.PublicKey) (*ecdsa.PublicKey, error) { 58 | switch pk := pubKey.(type) { 59 | case *secp256k1.PublicKey: 60 | return pk.ECDSA(), nil 61 | default: 62 | return nil, errors.New("unknown public key") 63 | } 64 | } 65 | 66 | func (kx SECP256K1) privateKey(privKey crypto.PrivateKey) (*ecdsa.PrivateKey, error) { 67 | switch pk := privKey.(type) { 68 | case *secp256k1.PrivateKey: 69 | return pk.ECDSA() 70 | default: 71 | return nil, errors.New("unknown private key") 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /cipher/aes256cbc/encoding.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mailchain/go-crypto/cipher" 7 | ) 8 | 9 | // bytesEncode encode the encrypted data to the hex format 10 | func bytesEncode(data *encryptedData) ([]byte, error) { 11 | if err := data.verify(); err != nil { 12 | return nil, fmt.Errorf("encrypted data is invalid: %w", err) 13 | } 14 | compressedKey, err := compress(data.EphemeralPublicKey) 15 | if err != nil { 16 | return nil, fmt.Errorf("could not compress EphemeralPublicKey: %w", err) 17 | } 18 | encodedData := make([]byte, 1+len(data.InitializationVector)+len(compressedKey)+len(data.MessageAuthenticationCode)+len(data.Ciphertext)) 19 | encodedData[0] = cipher.AES256CBC 20 | copy(encodedData[1:], data.InitializationVector) 21 | copy(encodedData[1+len(data.InitializationVector):], compressedKey) 22 | copy(encodedData[1+len(data.InitializationVector)+len(compressedKey):], data.MessageAuthenticationCode) 23 | copy(encodedData[1+len(data.InitializationVector)+len(compressedKey)+len(data.MessageAuthenticationCode):], data.Ciphertext) 24 | return encodedData, nil 25 | } 26 | 27 | // bytesDecode convert the hex format in to the encrypted data format 28 | func bytesDecode(raw []byte) (*encryptedData, error) { 29 | macLen := 32 30 | ivLen := 16 31 | if len(raw) == 0 { 32 | return nil, fmt.Errorf("raw must not be empty") 33 | } 34 | 35 | if len(raw) < macLen+ivLen+pubKeyBytesLenCompressed+1 { 36 | return nil, fmt.Errorf("raw data does not have enough bytes to be encoded") 37 | } 38 | 39 | if raw[0] != cipher.AES256CBC { 40 | return nil, fmt.Errorf("invalid prefix") 41 | } 42 | raw = raw[1:] 43 | decompressedKey := decompress(raw[ivLen : ivLen+pubKeyBytesLenCompressed]) 44 | // iv and mac must be created this way to ensure the cap of the array is not different 45 | iv := make([]byte, ivLen) 46 | copy(iv, raw[:ivLen]) 47 | mac := make([]byte, macLen) 48 | copy(mac, raw[ivLen+pubKeyBytesLenCompressed:ivLen+pubKeyBytesLenCompressed+macLen]) 49 | 50 | ret := &encryptedData{ 51 | InitializationVector: iv, 52 | EphemeralPublicKey: decompressedKey, 53 | MessageAuthenticationCode: mac, 54 | Ciphertext: raw[ivLen+pubKeyBytesLenCompressed+macLen:], 55 | } 56 | if err := ret.verify(); err != nil { 57 | return nil, fmt.Errorf("encrypted data is invalid: %w", err) 58 | } 59 | 60 | return ret, nil 61 | } 62 | -------------------------------------------------------------------------------- /cipher/ciphertest/keyexchange_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: keyexchange.go 3 | 4 | // Package ciphertest is a generated GoMock package. 5 | package ciphertest 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | gomock "github.com/golang/mock/gomock" 11 | crypto "github.com/mailchain/go-crypto" 12 | ) 13 | 14 | // MockKeyExchange is a mock of KeyExchange interface. 15 | type MockKeyExchange struct { 16 | ctrl *gomock.Controller 17 | recorder *MockKeyExchangeMockRecorder 18 | } 19 | 20 | // MockKeyExchangeMockRecorder is the mock recorder for MockKeyExchange. 21 | type MockKeyExchangeMockRecorder struct { 22 | mock *MockKeyExchange 23 | } 24 | 25 | // NewMockKeyExchange creates a new mock instance. 26 | func NewMockKeyExchange(ctrl *gomock.Controller) *MockKeyExchange { 27 | mock := &MockKeyExchange{ctrl: ctrl} 28 | mock.recorder = &MockKeyExchangeMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockKeyExchange) EXPECT() *MockKeyExchangeMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // EphemeralKey mocks base method. 38 | func (m *MockKeyExchange) EphemeralKey() (crypto.PrivateKey, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "EphemeralKey") 41 | ret0, _ := ret[0].(crypto.PrivateKey) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // EphemeralKey indicates an expected call of EphemeralKey. 47 | func (mr *MockKeyExchangeMockRecorder) EphemeralKey() *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EphemeralKey", reflect.TypeOf((*MockKeyExchange)(nil).EphemeralKey)) 50 | } 51 | 52 | // SharedSecret mocks base method. 53 | func (m *MockKeyExchange) SharedSecret(privateKey crypto.PrivateKey, publicKey crypto.PublicKey) ([]byte, error) { 54 | m.ctrl.T.Helper() 55 | ret := m.ctrl.Call(m, "SharedSecret", privateKey, publicKey) 56 | ret0, _ := ret[0].([]byte) 57 | ret1, _ := ret[1].(error) 58 | return ret0, ret1 59 | } 60 | 61 | // SharedSecret indicates an expected call of SharedSecret. 62 | func (mr *MockKeyExchangeMockRecorder) SharedSecret(privateKey, publicKey interface{}) *gomock.Call { 63 | mr.mock.ctrl.T.Helper() 64 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SharedSecret", reflect.TypeOf((*MockKeyExchange)(nil).SharedSecret), privateKey, publicKey) 65 | } 66 | -------------------------------------------------------------------------------- /multikey/ids_test.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | ) 11 | 12 | func TestIDFromPublicKey(t *testing.T) { 13 | type args struct { 14 | key crypto.PublicKey 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want byte 20 | wantErr bool 21 | }{ 22 | { 23 | "ed25519", 24 | args{ 25 | ed25519test.AlicePublicKey, 26 | }, 27 | 0xe2, 28 | false, 29 | }, 30 | { 31 | "secp256k1", 32 | args{ 33 | secp256k1test.AlicePublicKey, 34 | }, 35 | 0xe1, 36 | false, 37 | }, 38 | { 39 | "sr25519", 40 | args{ 41 | sr25519test.AlicePublicKey, 42 | }, 43 | 0xe3, 44 | false, 45 | }, 46 | } 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | got, err := IDFromPublicKey(tt.args.key) 50 | if (err != nil) != tt.wantErr { 51 | t.Errorf("IDFromPublicKey() error = %v, wantErr %v", err, tt.wantErr) 52 | return 53 | } 54 | if got != tt.want { 55 | t.Errorf("IDFromPublicKey() = %v, want %v", got, tt.want) 56 | } 57 | }) 58 | } 59 | } 60 | 61 | func TestIDFromPrivateKey(t *testing.T) { 62 | type args struct { 63 | key crypto.PrivateKey 64 | } 65 | tests := []struct { 66 | name string 67 | args args 68 | want byte 69 | wantErr bool 70 | }{ 71 | { 72 | "ed25519", 73 | args{ 74 | ed25519test.AlicePrivateKey, 75 | }, 76 | 0xe2, 77 | false, 78 | }, 79 | { 80 | "secp256k1", 81 | args{ 82 | secp256k1test.AlicePrivateKey, 83 | }, 84 | 0xe1, 85 | false, 86 | }, 87 | { 88 | "sr25519", 89 | args{ 90 | sr25519test.AlicePrivateKey, 91 | }, 92 | 0xe3, 93 | false, 94 | }, 95 | } 96 | for _, tt := range tests { 97 | t.Run(tt.name, func(t *testing.T) { 98 | got, err := IDFromPrivateKey(tt.args.key) 99 | if (err != nil) != tt.wantErr { 100 | t.Errorf("IDFromPrivateKey() error = %v, wantErr %v", err, tt.wantErr) 101 | return 102 | } 103 | if got != tt.want { 104 | t.Errorf("IDFromPrivateKey() = %v, want %v", got, tt.want) 105 | } 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /ed25519/ed25519test/keys.go: -------------------------------------------------------------------------------- 1 | // Package ed25519test contains keys that are used to ease testing of Mailchain 2 | // functionality. 3 | // All keys in this package are publicly known and therefore compromised. Keys 4 | // MUST not be used on any live networks, as secrets, or for any purpose other 5 | // than creating a reproducible unsecured test. 6 | 7 | package ed25519test 8 | 9 | import ( 10 | "log" 11 | 12 | "github.com/mailchain/go-crypto" 13 | "github.com/mailchain/go-crypto/ed25519" 14 | "github.com/mailchain/go-encoding/encodingtest" 15 | ) 16 | 17 | // AlicePrivateKey ed25519 key for testing purposes. Key is compromised do not use on mainnet's. 18 | var AlicePrivateKey crypto.PrivateKey //nolint: gochecknoglobals 19 | // AlicePublicKey ed25519 key for testing purposes. Key is compromised do not use on mainnet's. 20 | var AlicePublicKey crypto.PublicKey //nolint: gochecknoglobals 21 | // BobPrivateKey ed25519 key for testing purposes. Key is compromised do not use on mainnet's. 22 | var BobPrivateKey crypto.PrivateKey //nolint: gochecknoglobals 23 | // BobPublicKey ed25519 key for testing purposes. Key is compromised do not use on mainnet's. 24 | var BobPublicKey crypto.PublicKey //nolint: gochecknoglobals 25 | 26 | // CharliePrivateKey ed25519 key for testing purposes. Key is compromised do not use on mainnet's. 27 | var CharliePrivateKey crypto.PrivateKey //nolint: gochecknoglobals 28 | // CharliePublicKey ed25519 key for testing purposes. Key is compromised do not use on mainnet's. 29 | var CharliePublicKey crypto.PublicKey //nolint: gochecknoglobals 30 | 31 | // nolint: gochecknoinits 32 | func init() { 33 | var err error 34 | AlicePrivateKey, err = ed25519.PrivateKeyFromBytes(encodingtest.MustDecodeHex("0d9b4a3c10721991c6b806f0f343535dc2b46c74bece50a0a0d6b9f0070d3157")) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | AlicePublicKey = AlicePrivateKey.PublicKey() 40 | 41 | BobPrivateKey, err = ed25519.PrivateKeyFromBytes(encodingtest.MustDecodeHex("39d4c97d6a7f9e3306a2b5aae604ee67ec8b1387fffb39128fc055656cff05bb")) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | BobPublicKey = BobPrivateKey.PublicKey() 47 | 48 | CharliePrivateKey, err = ed25519.PrivateKeyFromBytes(encodingtest.MustDecodeHex("cd81ad6a71da3cbe070c6e73a6ab591a9987a3e6ce2ba2ef6c2a3846ed3cdb08")) 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | 53 | CharliePublicKey = CharliePrivateKey.PublicKey() 54 | } 55 | -------------------------------------------------------------------------------- /secp256r1/secp256r1test/keys.go: -------------------------------------------------------------------------------- 1 | // Package secp256k1test contains keys that are used to ease testing of Mailchain 2 | // functionality. 3 | // All keys in this package are publicly known and therefore compromised. Keys 4 | // MUST not be used on any live networks, as secrets, or for any purpose other 5 | // than creating a reproducible unsecured test. 6 | package secp256r1test 7 | 8 | import ( 9 | "log" 10 | 11 | "github.com/mailchain/go-crypto" 12 | "github.com/mailchain/go-crypto/secp256r1" 13 | "github.com/mailchain/go-encoding/encodingtest" 14 | ) 15 | 16 | // AlicePrivateKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 17 | var AlicePrivateKey crypto.PrivateKey //nolint: gochecknoglobals 18 | // AlicePublicKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 19 | var AlicePublicKey crypto.PublicKey //nolint: gochecknoglobals 20 | // BobPrivateKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 21 | var BobPrivateKey crypto.PrivateKey //nolint: gochecknoglobals 22 | // BobPublicKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 23 | var BobPublicKey crypto.PublicKey //nolint: gochecknoglobals 24 | 25 | // CarlosPrivateKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 26 | var CarlosPrivateKey crypto.PrivateKey //nolint: gochecknoglobals 27 | // CarlosPublicKey secp256k1 key for testing purposes. Key is compromised do not use on mainnet's. 28 | var CarlosPublicKey crypto.PublicKey //nolint: gochecknoglobals 29 | 30 | // nolint: gochecknoinits 31 | func init() { 32 | var err error 33 | 34 | AlicePrivateKey, err = secp256r1.PrivateKeyFromBytes(encodingtest.MustDecodeHex("3cdee0ff28337463455cd1cc43d29b1bf749d9615576525853ccc02b83c8b433")) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | 39 | AlicePublicKey = AlicePrivateKey.PublicKey() 40 | 41 | BobPrivateKey, err = secp256r1.PrivateKeyFromBytes(encodingtest.MustDecodeHex("a1e65c4677435cea57950b39379a9ec7ec0c64edc97efe36cdaae3c386fe2b71")) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | BobPublicKey = BobPrivateKey.PublicKey() 47 | 48 | CarlosPrivateKey, err = secp256r1.PrivateKeyFromBytes(encodingtest.MustDecodeHex("7198ec54092518b49b2c66468a058f1fdbf0fdf0b1e281a027c692bb0ee1d1ed")) 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | 53 | CarlosPublicKey = CarlosPrivateKey.PublicKey() 54 | 55 | } 56 | -------------------------------------------------------------------------------- /multikey/names_test.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | ) 11 | 12 | func TestKindFromPublicKey(t *testing.T) { 13 | type args struct { 14 | key crypto.PublicKey 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want string 20 | wantErr bool 21 | }{ 22 | { 23 | "ed25519", 24 | args{ 25 | ed25519test.AlicePublicKey, 26 | }, 27 | "ed25519", 28 | false, 29 | }, 30 | { 31 | "secp256k1", 32 | args{ 33 | secp256k1test.AlicePublicKey, 34 | }, 35 | "secp256k1", 36 | false, 37 | }, 38 | { 39 | "sr25519", 40 | args{ 41 | sr25519test.AlicePublicKey, 42 | }, 43 | "sr25519", 44 | false, 45 | }, 46 | } 47 | for _, tt := range tests { 48 | t.Run(tt.name, func(t *testing.T) { 49 | got, err := KindFromPublicKey(tt.args.key) 50 | if (err != nil) != tt.wantErr { 51 | t.Errorf("KindFromPublicKey() error = %v, wantErr %v", err, tt.wantErr) 52 | return 53 | } 54 | if got != tt.want { 55 | t.Errorf("KindFromPublicKey() = %v, want %v", got, tt.want) 56 | } 57 | }) 58 | } 59 | } 60 | 61 | func TestKindFromPrivateKey(t *testing.T) { 62 | type args struct { 63 | key crypto.PrivateKey 64 | } 65 | tests := []struct { 66 | name string 67 | args args 68 | want string 69 | wantErr bool 70 | }{ 71 | { 72 | "ed25519", 73 | args{ 74 | ed25519test.AlicePrivateKey, 75 | }, 76 | "ed25519", 77 | false, 78 | }, 79 | { 80 | "secp256k1", 81 | args{ 82 | secp256k1test.AlicePrivateKey, 83 | }, 84 | "secp256k1", 85 | false, 86 | }, 87 | { 88 | "sr25519", 89 | args{ 90 | sr25519test.AlicePrivateKey, 91 | }, 92 | "sr25519", 93 | false, 94 | }, 95 | } 96 | for _, tt := range tests { 97 | t.Run(tt.name, func(t *testing.T) { 98 | got, err := KindFromPrivateKey(tt.args.key) 99 | if (err != nil) != tt.wantErr { 100 | t.Errorf("KindFromPrivateKey() error = %v, wantErr %v", err, tt.wantErr) 101 | return 102 | } 103 | if got != tt.want { 104 | t.Errorf("KindFromPrivateKey() = %v, want %v", got, tt.want) 105 | } 106 | }) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /cryptotest/public_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: public.go 3 | 4 | // Package cryptotest is a generated GoMock package. 5 | package cryptotest 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | gomock "github.com/golang/mock/gomock" 11 | ) 12 | 13 | // MockPublicKey is a mock of PublicKey interface. 14 | type MockPublicKey struct { 15 | ctrl *gomock.Controller 16 | recorder *MockPublicKeyMockRecorder 17 | } 18 | 19 | // MockPublicKeyMockRecorder is the mock recorder for MockPublicKey. 20 | type MockPublicKeyMockRecorder struct { 21 | mock *MockPublicKey 22 | } 23 | 24 | // NewMockPublicKey creates a new mock instance. 25 | func NewMockPublicKey(ctrl *gomock.Controller) *MockPublicKey { 26 | mock := &MockPublicKey{ctrl: ctrl} 27 | mock.recorder = &MockPublicKeyMockRecorder{mock} 28 | return mock 29 | } 30 | 31 | // EXPECT returns an object that allows the caller to indicate expected use. 32 | func (m *MockPublicKey) EXPECT() *MockPublicKeyMockRecorder { 33 | return m.recorder 34 | } 35 | 36 | // Bytes mocks base method. 37 | func (m *MockPublicKey) Bytes() []byte { 38 | m.ctrl.T.Helper() 39 | ret := m.ctrl.Call(m, "Bytes") 40 | ret0, _ := ret[0].([]byte) 41 | return ret0 42 | } 43 | 44 | // Bytes indicates an expected call of Bytes. 45 | func (mr *MockPublicKeyMockRecorder) Bytes() *gomock.Call { 46 | mr.mock.ctrl.T.Helper() 47 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bytes", reflect.TypeOf((*MockPublicKey)(nil).Bytes)) 48 | } 49 | 50 | // Kind mocks base method. 51 | func (m *MockPublicKey) Kind() string { 52 | m.ctrl.T.Helper() 53 | ret := m.ctrl.Call(m, "Kind") 54 | ret0, _ := ret[0].(string) 55 | return ret0 56 | } 57 | 58 | // Kind indicates an expected call of Kind. 59 | func (mr *MockPublicKeyMockRecorder) Kind() *gomock.Call { 60 | mr.mock.ctrl.T.Helper() 61 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Kind", reflect.TypeOf((*MockPublicKey)(nil).Kind)) 62 | } 63 | 64 | // Verify mocks base method. 65 | func (m *MockPublicKey) Verify(message, sig []byte) bool { 66 | m.ctrl.T.Helper() 67 | ret := m.ctrl.Call(m, "Verify", message, sig) 68 | ret0, _ := ret[0].(bool) 69 | return ret0 70 | } 71 | 72 | // Verify indicates an expected call of Verify. 73 | func (mr *MockPublicKeyMockRecorder) Verify(message, sig interface{}) *gomock.Call { 74 | mr.mock.ctrl.T.Helper() 75 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Verify", reflect.TypeOf((*MockPublicKey)(nil).Verify), message, sig) 76 | } 77 | -------------------------------------------------------------------------------- /cipher/ecdh/ed25519.go: -------------------------------------------------------------------------------- 1 | package ecdh 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | 8 | "github.com/agl/ed25519/extra25519" 9 | "github.com/mailchain/go-crypto" 10 | "github.com/mailchain/go-crypto/ed25519" 11 | "golang.org/x/crypto/curve25519" 12 | ) 13 | 14 | type ED25519 struct { 15 | rand io.Reader 16 | } 17 | 18 | func NewED25519(rand io.Reader) (*ED25519, error) { 19 | if rand == nil { 20 | return nil, errors.New("rand must not be nil") 21 | } 22 | 23 | return &ED25519{rand: rand}, nil 24 | } 25 | 26 | func (kx ED25519) EphemeralKey() (crypto.PrivateKey, error) { 27 | return ed25519.GenerateKey(kx.rand) 28 | } 29 | 30 | // SharedSecret computes a secret value from a private / public key pair. 31 | // On sending a message the private key should be an ephemeralKey or generated private key, 32 | // the public key is the recipient public key. 33 | // On reading a message the private key is the recipient private key, the public key is the 34 | // ephemeralKey or generated public key. 35 | func (kx ED25519) SharedSecret(privateKey crypto.PrivateKey, publicKey crypto.PublicKey) ([]byte, error) { 36 | privateKeyBytes, err := kx.privateKey(privateKey) 37 | if err != nil { 38 | return nil, ErrSharedSecretGenerate 39 | } 40 | 41 | publicKeyBytes, err := kx.publicKey(publicKey) 42 | if err != nil { 43 | return nil, ErrSharedSecretGenerate 44 | } 45 | 46 | ephemeralPublicKey, _ := kx.publicKey(privateKey.PublicKey()) 47 | 48 | if bytes.Equal(ephemeralPublicKey[:], publicKeyBytes[:]) { 49 | return nil, ErrSharedSecretGenerate 50 | } 51 | 52 | var secret [32]byte 53 | 54 | curve25519.ScalarMult(&secret, &privateKeyBytes, &publicKeyBytes) 55 | 56 | return secret[:], nil 57 | } 58 | 59 | func (kx ED25519) publicKey(pubKey crypto.PublicKey) (key [32]byte, err error) { 60 | switch pk := pubKey.(type) { 61 | case *ed25519.PublicKey: 62 | var ed25519Key, key [32]byte 63 | 64 | copy(ed25519Key[:], pk.Bytes()) 65 | extra25519.PublicKeyToCurve25519(&key, &ed25519Key) 66 | 67 | return key, nil 68 | default: 69 | return [32]byte{}, ErrSharedSecretGenerate 70 | } 71 | } 72 | 73 | func (kx ED25519) privateKey(privKey crypto.PrivateKey) (key [32]byte, err error) { 74 | switch pk := privKey.(type) { 75 | case *ed25519.PrivateKey: 76 | var ed25519Key [64]byte 77 | 78 | copy(ed25519Key[:], pk.Key.Seed()) 79 | extra25519.PrivateKeyToCurve25519(&key, &ed25519Key) 80 | 81 | return key, nil 82 | default: 83 | return [32]byte{}, ErrSharedSecretGenerate 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sr25519/sr25519test/keys.go: -------------------------------------------------------------------------------- 1 | // Package sr25519test contains keys that are used to ease testing of Mailchain 2 | // functionality. 3 | // All keys in this package are publicly known and therefore compromised. Keys 4 | // MUST not be used on any live networks, as secrets, or for any purpose other 5 | // than creating a reproducible unsecured test. 6 | package sr25519test 7 | 8 | import ( 9 | "log" 10 | 11 | "github.com/mailchain/go-crypto" 12 | "github.com/mailchain/go-crypto/sr25519" 13 | "github.com/mailchain/go-encoding/encodingtest" 14 | ) 15 | 16 | // AlicePrivateKey sr25519 key for testing purposes. Key is compromised do not use on mainnet's. 17 | var AlicePrivateKey crypto.PrivateKey //nolint: gochecknoglobals test key 18 | // AlicePublicKey sr25519 key for testing purposes. Key is compromised do not use on mainnet's. 19 | var AlicePublicKey crypto.PublicKey //nolint: gochecknoglobals test key 20 | // BobPrivateKey sr25519 key for testing purposes. Key is compromised do not use on mainnet's. 21 | var BobPrivateKey crypto.PrivateKey //nolint: gochecknoglobals test key 22 | // BobPublicKey sr25519 key for testing purposes. Key is compromised do not use on mainnet's. 23 | var BobPublicKey crypto.PublicKey //nolint: gochecknoglobals test key 24 | // EvePrivateKey sr25519 key for testing purposes. Key is compromised do not use on mainnet's. 25 | var EvePrivateKey crypto.PrivateKey //nolint: gochecknoglobals test key 26 | // EvePublicKey sr25519 key for testing purposes. Key is compromised do not use on mainnet's. 27 | var EvePublicKey crypto.PublicKey //nolint: gochecknoglobals test key 28 | 29 | // nolint: gochecknoinits test key 30 | func init() { 31 | var err error 32 | AlicePrivateKey, err = sr25519.PrivateKeyFromBytes(encodingtest.MustDecodeHex("5c6d7adf75bda1180c225d25f3aa8dc174bbfb3cddee11ae9a85982f6faf791a")) //nolint: lll test key 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | AlicePublicKey = AlicePrivateKey.PublicKey() 38 | 39 | BobPrivateKey, err = sr25519.PrivateKeyFromBytes(encodingtest.MustDecodeHex("23b063a581fd8e5e847c4e2b9c494247298791530f5293be369e8bf23a45d2bd")) //nolint: lll test key 40 | if err != nil { 41 | log.Fatal(err) 42 | } 43 | 44 | BobPublicKey = BobPrivateKey.PublicKey() 45 | 46 | EvePrivateKey, err = sr25519.PrivateKeyFromBytes(encodingtest.MustDecodeHex("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f")) //nolint: lll test key 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | 51 | EvePublicKey = EvePrivateKey.PublicKey() 52 | } 53 | -------------------------------------------------------------------------------- /secp256k1/private.go: -------------------------------------------------------------------------------- 1 | package secp256k1 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "math/big" 9 | 10 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 11 | "github.com/ethereum/go-ethereum/crypto/ecies" 12 | "github.com/mailchain/go-crypto" 13 | ) 14 | 15 | var ( 16 | 17 | // ErrUnusableSeed describes an error in which the provided seed is not 18 | // usable due to the derived key falling outside of the valid range for 19 | // secp256k1 private keys. This error indicates the caller must choose 20 | // another seed. 21 | ErrUnusableSeed = errors.New("unusable seed") 22 | ) 23 | 24 | // PrivateKey based on the secp256k1 curve. 25 | type PrivateKey struct { 26 | ecdsa ecdsa.PrivateKey 27 | } 28 | 29 | // Bytes returns the byte representation of the private key. 30 | func (pk PrivateKey) Bytes() []byte { 31 | return ethcrypto.FromECDSA(&pk.ecdsa) 32 | } 33 | 34 | // TODO: remove this sign function 35 | // Sign signs the message with the private key and returns the signature. 36 | func (pk PrivateKey) Sign(message []byte) (signature []byte, err error) { 37 | return ethcrypto.Sign(message[:], &pk.ecdsa) 38 | } 39 | 40 | // PublicKey return the public key that is derived from the private key. 41 | func (pk PrivateKey) PublicKey() crypto.PublicKey { 42 | return &PublicKey{ecdsa: pk.ecdsa.PublicKey} 43 | } 44 | 45 | // ECIES returns an ECIES representation of the private key. 46 | func (pk PrivateKey) ECIES() *ecies.PrivateKey { 47 | return ecies.ImportECDSA(&pk.ecdsa) 48 | } 49 | 50 | // ECDSA returns an ECDSA representation of the private key. 51 | func (pk PrivateKey) ECDSA() (*ecdsa.PrivateKey, error) { 52 | rpk, err := ethcrypto.ToECDSA(pk.Bytes()) 53 | if err != nil { 54 | return nil, fmt.Errorf("could not convert private key: %w", err) 55 | } 56 | return rpk, nil 57 | } 58 | 59 | // PrivateKeyFromECDSA get a private key from an ecdsa.PrivateKey. 60 | func PrivateKeyFromECDSA(pk ecdsa.PrivateKey) PrivateKey { 61 | return PrivateKey{ecdsa: pk} 62 | } 63 | 64 | // PrivateKeyFromBytes get a private key from []byte. 65 | func PrivateKeyFromBytes(pk []byte) (*PrivateKey, error) { 66 | // Ensure the private key is valid. It must be within the range 67 | // of the order of the secp256k1 curve and not be 0. 68 | keyNum := new(big.Int).SetBytes(pk) 69 | if keyNum.Cmp(ethcrypto.S256().Params().N) >= 0 || keyNum.Sign() == 0 { 70 | return nil, ErrUnusableSeed 71 | } 72 | 73 | rpk, err := ethcrypto.ToECDSA(pk) 74 | if err != nil { 75 | return nil, fmt.Errorf("could not convert private key") 76 | } 77 | 78 | return &PrivateKey{ecdsa: *rpk}, nil 79 | } 80 | 81 | func GenerateKey(rand io.Reader) (*PrivateKey, error) { 82 | pk, err := ecdsa.GenerateKey(ethcrypto.S256(), rand) 83 | if err != nil { 84 | return nil, err 85 | } 86 | 87 | return &PrivateKey{ecdsa: *pk}, nil 88 | } 89 | -------------------------------------------------------------------------------- /secp256r1/keys_test.go: -------------------------------------------------------------------------------- 1 | package secp256r1 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "log" 6 | 7 | "github.com/mailchain/go-encoding/encodingtest" 8 | ) 9 | 10 | var ( 11 | aliceSECP256R1PrivateKey = func() PrivateKey { 12 | k, err := PrivateKeyFromBytes(aliceSECP256R1PrivateKeyBytes) 13 | if err != nil { 14 | log.Fatal(err) 15 | } 16 | return *k 17 | }() //nolint: lll 18 | 19 | aliceSECP256R1PrivateKeyBytes = encodingtest.MustDecodeHex("3cdee0ff28337463455cd1cc43d29b1bf749d9615576525853ccc02b83c8b433") 20 | 21 | aliceSECP256R1PrivateECDSA = func() ecdsa.PrivateKey { 22 | key, err := toECDSA(aliceSECP256R1PrivateKeyBytes) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | return *key 27 | } 28 | 29 | aliceSECP256R1PublicKey = func() PublicKey { 30 | k, err := PublicKeyFromBytes(aliceSECP256R1PublicKeyBytes) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | 35 | return *k.(*PublicKey) 36 | }() 37 | aliceSECP256R1PublicKeyBytes = encodingtest.MustDecodeHex("0330ef59d5da4547c684aa0d5b7d8c1527fceab462cfd8d4a3529319c469b0d4d7") 38 | ) 39 | 40 | var ( 41 | bobSECP256R1PrivateKey = func() PrivateKey { 42 | k, err := PrivateKeyFromBytes(bobSECP256R1PrivateKeyBytes) 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | return *k 47 | }() //nolint: lll 48 | 49 | bobSECP256R1PrivateKeyBytes = encodingtest.MustDecodeHex("a1e65c4677435cea57950b39379a9ec7ec0c64edc97efe36cdaae3c386fe2b71") 50 | 51 | bobSECP256R1PrivateECDSA = func() ecdsa.PrivateKey { 52 | key, err := toECDSA(bobSECP256R1PrivateKeyBytes) 53 | if err != nil { 54 | log.Fatal(err) 55 | } 56 | return *key 57 | } 58 | bobSECP256R1PublicKey = func() PublicKey { 59 | k, err := PublicKeyFromBytes(bobSECP256R1PublicKeyBytes) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | 64 | return *k.(*PublicKey) 65 | }() 66 | bobSECP256R1PublicKeyBytes = encodingtest.MustDecodeHex("032da43f4b992968e53c68c894933e8ba22a7905bf9cdc903fd96d4f38ff49e115") 67 | ) 68 | 69 | var ( 70 | carlosSECP256R1PrivateKey = func() PrivateKey { 71 | k, err := PrivateKeyFromBytes(carlosSECP256R1PrivateKeyBytes) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | return *k 76 | }() //nolint: lll 77 | 78 | carlosSECP256R1PrivateKeyBytes = encodingtest.MustDecodeHex("7198ec54092518b49b2c66468a058f1fdbf0fdf0b1e281a027c692bb0ee1d1ed") 79 | carlosSECP256R1PrivateECDSA = func() ecdsa.PrivateKey { 80 | key, err := toECDSA(carlosSECP256R1PrivateKeyBytes) 81 | if err != nil { 82 | log.Fatal(err) 83 | } 84 | return *key 85 | } 86 | carlosSECP256R1PublicKey = func() PublicKey { 87 | k, err := PublicKeyFromBytes(carlosSECP256R1PublicKeyBytes) 88 | if err != nil { 89 | log.Fatal(err) 90 | } 91 | 92 | return *k.(*PublicKey) 93 | }() 94 | carlosSECP256R1PublicKeyBytes = encodingtest.MustDecodeHex("02c48a6a32004a6ec31b78c05c9ea9d6bee0904f7f7a13f384c6f2a25c86fcb0e6") 95 | ) 96 | -------------------------------------------------------------------------------- /cipher/aes256cbc/end_end_test.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "testing" 7 | 8 | "github.com/mailchain/go-crypto" 9 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestEncryptDecrypt(t *testing.T) { 14 | cases := []struct { 15 | name string 16 | recipientPublicKey crypto.PublicKey 17 | recipientPrivateKey crypto.PrivateKey 18 | data []byte 19 | wantEncryptErr bool 20 | wantDecryptErr bool 21 | wantDecrypt bool 22 | }{ 23 | { 24 | "success-to-alice-short-text", 25 | secp256k1test.AlicePublicKey, 26 | secp256k1test.AlicePrivateKey, 27 | []byte("Hi Sofia"), 28 | false, 29 | false, 30 | true, 31 | }, 32 | { 33 | "success-to-alice-medium-text", 34 | secp256k1test.AlicePublicKey, 35 | secp256k1test.AlicePrivateKey, 36 | []byte("Hi Sofia, this is a little bit of a longer message to make sure there are no problems"), 37 | false, 38 | false, 39 | true, 40 | }, 41 | { 42 | "success-to-bob-short-text", 43 | secp256k1test.BobPublicKey, 44 | secp256k1test.BobPrivateKey, 45 | []byte("Hi Charlotte"), 46 | false, 47 | false, 48 | true, 49 | }, 50 | { 51 | "success-to-bob-medium-text", 52 | secp256k1test.BobPublicKey, 53 | secp256k1test.BobPrivateKey, 54 | []byte("Hi Charlotte, this is a little bit of a longer message to make sure there are no problems"), 55 | false, 56 | false, 57 | true, 58 | }, 59 | { 60 | "err-alice-with-bob", 61 | secp256k1test.AlicePublicKey, 62 | secp256k1test.BobPrivateKey, 63 | []byte("Hi Sofia"), 64 | false, 65 | true, 66 | false, 67 | }, 68 | { 69 | "err-bob-with-alice", 70 | secp256k1test.BobPublicKey, 71 | secp256k1test.AlicePrivateKey, 72 | []byte("Hi Sofia"), 73 | false, 74 | true, 75 | false, 76 | }, 77 | } 78 | 79 | for _, tt := range cases { 80 | t.Run(tt.name, func(t *testing.T) { 81 | encrypter := Encrypter{rand.Reader, tt.recipientPublicKey} 82 | encrypted, err := encrypter.Encrypt(tt.data) 83 | if (err != nil) != tt.wantEncryptErr { 84 | t.Errorf("Encrypt() error = %v, wantEncryptErr %v", err, tt.wantEncryptErr) 85 | return 86 | } 87 | assert.NotNil(t, encrypted) 88 | 89 | decrypter := Decrypter{tt.recipientPrivateKey} 90 | decrypted, err := decrypter.Decrypt(encrypted) 91 | if (err != nil) != tt.wantDecryptErr { 92 | t.Errorf("Decrypt() error = %v, wantDecryptErr %v", err, tt.wantDecryptErr) 93 | return 94 | } 95 | 96 | if bytes.Equal(tt.data, []byte(decrypted)) != tt.wantDecrypt { 97 | t.Errorf("Decrypt() result = %v, wantDecrypt %v", err, tt.wantDecrypt) 98 | return 99 | } 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /ed25519/end_end_test.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestSignVerify(t *testing.T) { 10 | cases := []struct { 11 | name string 12 | signedBy PrivateKey 13 | verifiedBy PublicKey 14 | message []byte 15 | wantSig []byte 16 | wantErr bool 17 | wantVerified bool 18 | }{ 19 | { 20 | "bob-private-public-key", 21 | bobPrivateKey, 22 | bobPublicKey, 23 | []byte("message"), 24 | []byte{0x7d, 0x51, 0xea, 0xfa, 0x52, 0x78, 0x31, 0x69, 0xd0, 0xa9, 0x4a, 0xc, 0x9f, 0x2b, 0xca, 0xd5, 0xe0, 0x3d, 0x29, 0x17, 0x33, 0x0, 0x93, 0xf, 0xf3, 0xc7, 0xd6, 0x3b, 0xfd, 0x64, 0x17, 0xae, 0x1b, 0xc8, 0x1f, 0xef, 0x51, 0xba, 0x14, 0x9a, 0xe8, 0xa1, 0xe1, 0xda, 0xe0, 0x5f, 0xdc, 0xa5, 0x7, 0x8b, 0x14, 0xba, 0xc4, 0xcf, 0x26, 0xcc, 0xc6, 0x1, 0x1e, 0x5e, 0xab, 0x77, 0x3, 0xc}, 25 | false, 26 | true, 27 | }, 28 | { 29 | "alice-private-public-key", 30 | alicePrivateKey, 31 | alicePublicKey, 32 | []byte("egassem"), 33 | []byte{0xde, 0x6c, 0x88, 0xe6, 0x9c, 0x9f, 0x93, 0xb, 0x59, 0xdd, 0xf4, 0x80, 0xc2, 0x9a, 0x55, 0x79, 0xec, 0x89, 0x5c, 0xa9, 0x7a, 0x36, 0xf6, 0x69, 0x74, 0xc1, 0xf0, 0x15, 0x5c, 0xc0, 0x66, 0x75, 0x2e, 0xcd, 0x9a, 0x9b, 0x41, 0x35, 0xd2, 0x72, 0x32, 0xe0, 0x54, 0x80, 0xbc, 0x98, 0x58, 0x1, 0xa9, 0xfd, 0xe4, 0x27, 0xc7, 0xef, 0xa5, 0x42, 0x5f, 0xf, 0x46, 0x49, 0xb8, 0xad, 0xbd, 0x5}, 34 | false, 35 | true, 36 | }, 37 | { 38 | "alice-private-bob-public-key", 39 | alicePrivateKey, 40 | bobPublicKey, 41 | []byte("egassem"), 42 | []byte{0xde, 0x6c, 0x88, 0xe6, 0x9c, 0x9f, 0x93, 0xb, 0x59, 0xdd, 0xf4, 0x80, 0xc2, 0x9a, 0x55, 0x79, 0xec, 0x89, 0x5c, 0xa9, 0x7a, 0x36, 0xf6, 0x69, 0x74, 0xc1, 0xf0, 0x15, 0x5c, 0xc0, 0x66, 0x75, 0x2e, 0xcd, 0x9a, 0x9b, 0x41, 0x35, 0xd2, 0x72, 0x32, 0xe0, 0x54, 0x80, 0xbc, 0x98, 0x58, 0x1, 0xa9, 0xfd, 0xe4, 0x27, 0xc7, 0xef, 0xa5, 0x42, 0x5f, 0xf, 0x46, 0x49, 0xb8, 0xad, 0xbd, 0x5}, 43 | false, 44 | false, 45 | }, 46 | { 47 | "bob-private-alice-public-key", 48 | bobPrivateKey, 49 | alicePublicKey, 50 | []byte("message"), 51 | []byte{0x7d, 0x51, 0xea, 0xfa, 0x52, 0x78, 0x31, 0x69, 0xd0, 0xa9, 0x4a, 0xc, 0x9f, 0x2b, 0xca, 0xd5, 0xe0, 0x3d, 0x29, 0x17, 0x33, 0x0, 0x93, 0xf, 0xf3, 0xc7, 0xd6, 0x3b, 0xfd, 0x64, 0x17, 0xae, 0x1b, 0xc8, 0x1f, 0xef, 0x51, 0xba, 0x14, 0x9a, 0xe8, 0xa1, 0xe1, 0xda, 0xe0, 0x5f, 0xdc, 0xa5, 0x7, 0x8b, 0x14, 0xba, 0xc4, 0xcf, 0x26, 0xcc, 0xc6, 0x1, 0x1e, 0x5e, 0xab, 0x77, 0x3, 0xc}, 52 | false, 53 | false, 54 | }, 55 | } 56 | for _, tc := range cases { 57 | t.Run(tc.name, func(t *testing.T) { 58 | gotSig, err := tc.signedBy.Sign(tc.message) 59 | assert.Equal(t, tc.wantErr, err != nil) 60 | assert.Equal(t, tc.wantSig, gotSig) 61 | 62 | gotVerified := tc.verifiedBy.Verify(tc.message, gotSig) 63 | assert.Equal(t, tc.wantVerified, gotVerified) 64 | }) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cipher/aes256cbc/encrypter.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/elliptic" 7 | "crypto/rand" 8 | "io" 9 | 10 | "github.com/andreburgaud/crypt2go/padding" 11 | "github.com/ethereum/go-ethereum/crypto/ecies" 12 | "github.com/mailchain/go-crypto" 13 | mc "github.com/mailchain/go-crypto/cipher" 14 | ) 15 | 16 | // NewEncrypter create a new encrypter with crypto rand for reader 17 | // and attaching the public key to the encrypter. 18 | func NewEncrypter(pubKey crypto.PublicKey) (*Encrypter, error) { 19 | return &Encrypter{rand: rand.Reader, publicKey: pubKey}, nil 20 | } 21 | 22 | // Encrypter will encrypt data using AES256CBC method 23 | type Encrypter struct { 24 | rand io.Reader 25 | publicKey crypto.PublicKey 26 | } 27 | 28 | // Encrypt data using recipient public key with AES in CBC mode. Generate an ephemeral private key and IV. 29 | func (e Encrypter) Encrypt(message mc.PlainContent) (mc.EncryptedContent, error) { 30 | epk, err := asPublicECIES(e.publicKey) 31 | if err != nil { 32 | return nil, mc.ErrEncrypt 33 | } 34 | 35 | ephemeral, err := ecies.GenerateKey(e.rand, ecies.DefaultCurve, nil) 36 | if err != nil { 37 | return nil, mc.ErrEncrypt 38 | } 39 | 40 | iv, err := e.generateIV() 41 | if err != nil { 42 | return nil, mc.ErrEncrypt 43 | } 44 | 45 | encryptedData, err := encrypt(ephemeral, epk, message, iv) 46 | if err != nil { 47 | return nil, mc.ErrEncrypt 48 | } 49 | 50 | return bytesEncode(encryptedData) 51 | } 52 | 53 | func encrypt(ephemeralPrivateKey *ecies.PrivateKey, pub *ecies.PublicKey, input, iv []byte) (*encryptedData, error) { 54 | ephemeralPublicKey := ephemeralPrivateKey.PublicKey 55 | sharedSecret, err := deriveSharedSecret(pub, ephemeralPrivateKey) 56 | if err != nil { 57 | return nil, err 58 | } 59 | macKey, encryptionKey := generateMacKeyAndEncryptionKey(sharedSecret) 60 | ciphertext, err := encryptCBC(input, iv, encryptionKey) 61 | if err != nil { 62 | return nil, mc.ErrEncrypt 63 | } 64 | 65 | mac, err := generateMac(macKey, iv, ephemeralPublicKey, ciphertext) 66 | if err != nil { 67 | return nil, mc.ErrEncrypt 68 | } 69 | 70 | return &encryptedData{ 71 | MessageAuthenticationCode: mac, 72 | InitializationVector: iv, 73 | EphemeralPublicKey: elliptic.Marshal(ecies.DefaultCurve, ephemeralPublicKey.X, ephemeralPublicKey.Y), 74 | Ciphertext: ciphertext, 75 | }, nil 76 | } 77 | 78 | func encryptCBC(data, iv, key []byte) ([]byte, error) { 79 | block, err := aes.NewCipher(key) 80 | if err != nil { 81 | return nil, err 82 | } 83 | data, err = padding.NewPkcs7Padding(block.BlockSize()).Pad(data) 84 | if err != nil { 85 | return nil, mc.ErrEncrypt 86 | } 87 | 88 | if len(iv) != block.BlockSize() { 89 | return nil, mc.ErrEncrypt 90 | } 91 | 92 | ciphertext := make([]byte, len(data)) 93 | cbc := cipher.NewCBCEncrypter(block, iv) 94 | cbc.CryptBlocks(ciphertext, data) 95 | 96 | return ciphertext, nil 97 | } 98 | -------------------------------------------------------------------------------- /cipher/aes256cbc/compression_test.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/mailchain/go-encoding/encodingtest" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCompress(t *testing.T) { 12 | cases := []struct { 13 | name string 14 | original []byte 15 | want []byte 16 | wantErr bool 17 | }{ 18 | {"no prefix:022c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292", 19 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cba"), 20 | encodingtest.MustDecodeHex("022c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 21 | false, 22 | }, 23 | {"with prefix:022c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292", 24 | encodingtest.MustDecodeHex("042c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cba"), 25 | encodingtest.MustDecodeHex("022c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 26 | false, 27 | }, 28 | {"err-invalid-key-length", 29 | encodingtest.MustDecodeHex("042c8432ca28ce929b86a47f2d40413d161f1f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cba"), 30 | nil, 31 | true, 32 | }, 33 | } 34 | for _, tt := range cases { 35 | t.Run(tt.name, func(t *testing.T) { 36 | got, err := compress(tt.original) 37 | if (err != nil) != tt.wantErr { 38 | t.Errorf("compress() error = %v, wantErr %v", err, tt.wantErr) 39 | return 40 | } 41 | if !assert.Equal(t, tt.want, got) { 42 | t.Errorf("compress() = %v, want %v", got, tt.want) 43 | } 44 | }) 45 | } 46 | } 47 | 48 | func Test_decompress(t *testing.T) { 49 | type args struct { 50 | publicKey []byte 51 | } 52 | tests := []struct { 53 | name string 54 | args args 55 | want []byte 56 | }{ 57 | { 58 | "022c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292", 59 | args{ 60 | encodingtest.MustDecodeHex("022c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 61 | }, 62 | encodingtest.MustDecodeHex("042c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cba"), 63 | }, 64 | { 65 | "03a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b", 66 | args{ 67 | encodingtest.MustDecodeHex("03a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b"), 68 | }, 69 | encodingtest.MustDecodeHex("04a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b4adf14868d8449c9b3e50d3d6338f3e5a2d3445abe679cddbe75cb893475806f"), 70 | }, 71 | } 72 | for _, tt := range tests { 73 | t.Run(tt.name, func(t *testing.T) { 74 | if got := decompress(tt.args.publicKey); !reflect.DeepEqual(got, tt.want) { 75 | t.Errorf("decompress() = %v, want %v", got, tt.want) 76 | } 77 | }) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /secp256k1/end_end_test.go: -------------------------------------------------------------------------------- 1 | package secp256k1 2 | 3 | import ( 4 | "crypto/sha256" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSignVerify(t *testing.T) { 11 | cases := []struct { 12 | name string 13 | signedBy PrivateKey 14 | verifiedBy PublicKey 15 | message []byte 16 | wantSig []byte 17 | wantErr bool 18 | wantVerified bool 19 | }{ 20 | { 21 | "bob-private-public-key", 22 | bobPrivateKey, 23 | bobPublicKey, 24 | []byte("message"), 25 | []byte{0x9d, 0xf7, 0x76, 0xab, 0xde, 0x8c, 0x20, 0x55, 0xc3, 0x4, 0x68, 0x37, 0xa8, 0x66, 0xf8, 0x89, 0x95, 0xf9, 0x82, 0xf0, 0x4b, 0xb8, 0x23, 0x40, 0xf0, 0x3, 0x8, 0x6a, 0x32, 0xa7, 0xac, 0xef, 0x5f, 0xa, 0xea, 0xda, 0x60, 0xbf, 0x9, 0xd5, 0xc3, 0x27, 0x61, 0xa, 0xc5, 0xc8, 0x33, 0xe3, 0xa0, 0x79, 0xdf, 0x6d, 0xe1, 0x9c, 0xa8, 0xcc, 0x33, 0xea, 0x1d, 0xe6, 0x3, 0x34, 0xb1, 0xa1, 0x0}, 26 | false, 27 | true, 28 | }, 29 | { 30 | "alice-private-public-key", 31 | alicePrivateKey, 32 | alicePublicKey, 33 | []byte("egassem"), 34 | []byte{0xe9, 0x33, 0xe, 0x4a, 0xe3, 0x5, 0x19, 0xea, 0x36, 0x37, 0x19, 0xdd, 0xbc, 0x91, 0xfd, 0x4f, 0xd3, 0x64, 0x9b, 0xdc, 0xf0, 0x74, 0x36, 0x16, 0xc9, 0x81, 0xfc, 0x6d, 0x3c, 0x7e, 0xb0, 0xd0, 0x6e, 0xdd, 0x4, 0x13, 0xfd, 0x15, 0xe5, 0xec, 0x64, 0x6e, 0x63, 0xe0, 0x84, 0xdb, 0xb2, 0xd7, 0xcf, 0x18, 0x3d, 0x81, 0x1e, 0x31, 0x36, 0x77, 0x39, 0x86, 0x4b, 0x58, 0xb8, 0x23, 0xed, 0xc, 0x1}, 35 | false, 36 | true, 37 | }, 38 | { 39 | "alice-private-bob-public-key", 40 | alicePrivateKey, 41 | bobPublicKey, 42 | []byte("egassem"), 43 | []byte{0xe9, 0x33, 0xe, 0x4a, 0xe3, 0x5, 0x19, 0xea, 0x36, 0x37, 0x19, 0xdd, 0xbc, 0x91, 0xfd, 0x4f, 0xd3, 0x64, 0x9b, 0xdc, 0xf0, 0x74, 0x36, 0x16, 0xc9, 0x81, 0xfc, 0x6d, 0x3c, 0x7e, 0xb0, 0xd0, 0x6e, 0xdd, 0x4, 0x13, 0xfd, 0x15, 0xe5, 0xec, 0x64, 0x6e, 0x63, 0xe0, 0x84, 0xdb, 0xb2, 0xd7, 0xcf, 0x18, 0x3d, 0x81, 0x1e, 0x31, 0x36, 0x77, 0x39, 0x86, 0x4b, 0x58, 0xb8, 0x23, 0xed, 0xc, 0x1}, 44 | false, 45 | false, 46 | }, 47 | { 48 | "bob-private-alice-public-key", 49 | bobPrivateKey, 50 | alicePublicKey, 51 | []byte("message"), 52 | []byte{0x9d, 0xf7, 0x76, 0xab, 0xde, 0x8c, 0x20, 0x55, 0xc3, 0x4, 0x68, 0x37, 0xa8, 0x66, 0xf8, 0x89, 0x95, 0xf9, 0x82, 0xf0, 0x4b, 0xb8, 0x23, 0x40, 0xf0, 0x3, 0x8, 0x6a, 0x32, 0xa7, 0xac, 0xef, 0x5f, 0xa, 0xea, 0xda, 0x60, 0xbf, 0x9, 0xd5, 0xc3, 0x27, 0x61, 0xa, 0xc5, 0xc8, 0x33, 0xe3, 0xa0, 0x79, 0xdf, 0x6d, 0xe1, 0x9c, 0xa8, 0xcc, 0x33, 0xea, 0x1d, 0xe6, 0x3, 0x34, 0xb1, 0xa1, 0x0}, 53 | false, 54 | false, 55 | }, 56 | } 57 | for _, tc := range cases { 58 | t.Run(tc.name, func(t *testing.T) { 59 | hash := sha256.Sum256(tc.message) 60 | gotSig, err := tc.signedBy.Sign(hash[:]) 61 | assert.Equal(t, tc.wantErr, err != nil) 62 | assert.Equal(t, tc.wantSig, gotSig) 63 | 64 | gotVerified := tc.verifiedBy.Verify(hash[:], gotSig) 65 | assert.Equal(t, tc.wantVerified, gotVerified) 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /cipher/ciphertest/cipher_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: cipher.go 3 | 4 | // Package ciphertest is a generated GoMock package. 5 | package ciphertest 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | gomock "github.com/golang/mock/gomock" 11 | cipher "github.com/mailchain/go-crypto/cipher" 12 | ) 13 | 14 | // MockDecrypter is a mock of Decrypter interface. 15 | type MockDecrypter struct { 16 | ctrl *gomock.Controller 17 | recorder *MockDecrypterMockRecorder 18 | } 19 | 20 | // MockDecrypterMockRecorder is the mock recorder for MockDecrypter. 21 | type MockDecrypterMockRecorder struct { 22 | mock *MockDecrypter 23 | } 24 | 25 | // NewMockDecrypter creates a new mock instance. 26 | func NewMockDecrypter(ctrl *gomock.Controller) *MockDecrypter { 27 | mock := &MockDecrypter{ctrl: ctrl} 28 | mock.recorder = &MockDecrypterMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockDecrypter) EXPECT() *MockDecrypterMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Decrypt mocks base method. 38 | func (m *MockDecrypter) Decrypt(arg0 cipher.EncryptedContent) (cipher.PlainContent, error) { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Decrypt", arg0) 41 | ret0, _ := ret[0].(cipher.PlainContent) 42 | ret1, _ := ret[1].(error) 43 | return ret0, ret1 44 | } 45 | 46 | // Decrypt indicates an expected call of Decrypt. 47 | func (mr *MockDecrypterMockRecorder) Decrypt(arg0 interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Decrypt", reflect.TypeOf((*MockDecrypter)(nil).Decrypt), arg0) 50 | } 51 | 52 | // MockEncrypter is a mock of Encrypter interface. 53 | type MockEncrypter struct { 54 | ctrl *gomock.Controller 55 | recorder *MockEncrypterMockRecorder 56 | } 57 | 58 | // MockEncrypterMockRecorder is the mock recorder for MockEncrypter. 59 | type MockEncrypterMockRecorder struct { 60 | mock *MockEncrypter 61 | } 62 | 63 | // NewMockEncrypter creates a new mock instance. 64 | func NewMockEncrypter(ctrl *gomock.Controller) *MockEncrypter { 65 | mock := &MockEncrypter{ctrl: ctrl} 66 | mock.recorder = &MockEncrypterMockRecorder{mock} 67 | return mock 68 | } 69 | 70 | // EXPECT returns an object that allows the caller to indicate expected use. 71 | func (m *MockEncrypter) EXPECT() *MockEncrypterMockRecorder { 72 | return m.recorder 73 | } 74 | 75 | // Encrypt mocks base method. 76 | func (m *MockEncrypter) Encrypt(arg0 cipher.PlainContent) (cipher.EncryptedContent, error) { 77 | m.ctrl.T.Helper() 78 | ret := m.ctrl.Call(m, "Encrypt", arg0) 79 | ret0, _ := ret[0].(cipher.EncryptedContent) 80 | ret1, _ := ret[1].(error) 81 | return ret0, ret1 82 | } 83 | 84 | // Encrypt indicates an expected call of Encrypt. 85 | func (mr *MockEncrypterMockRecorder) Encrypt(arg0 interface{}) *gomock.Call { 86 | mr.mock.ctrl.T.Helper() 87 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Encrypt", reflect.TypeOf((*MockEncrypter)(nil).Encrypt), arg0) 88 | } 89 | -------------------------------------------------------------------------------- /cryptotest/private_mock.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: private.go 3 | 4 | // Package cryptotest is a generated GoMock package. 5 | package cryptotest 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | gomock "github.com/golang/mock/gomock" 11 | crypto "github.com/mailchain/go-crypto" 12 | ) 13 | 14 | // MockPrivateKey is a mock of PrivateKey interface. 15 | type MockPrivateKey struct { 16 | ctrl *gomock.Controller 17 | recorder *MockPrivateKeyMockRecorder 18 | } 19 | 20 | // MockPrivateKeyMockRecorder is the mock recorder for MockPrivateKey. 21 | type MockPrivateKeyMockRecorder struct { 22 | mock *MockPrivateKey 23 | } 24 | 25 | // NewMockPrivateKey creates a new mock instance. 26 | func NewMockPrivateKey(ctrl *gomock.Controller) *MockPrivateKey { 27 | mock := &MockPrivateKey{ctrl: ctrl} 28 | mock.recorder = &MockPrivateKeyMockRecorder{mock} 29 | return mock 30 | } 31 | 32 | // EXPECT returns an object that allows the caller to indicate expected use. 33 | func (m *MockPrivateKey) EXPECT() *MockPrivateKeyMockRecorder { 34 | return m.recorder 35 | } 36 | 37 | // Bytes mocks base method. 38 | func (m *MockPrivateKey) Bytes() []byte { 39 | m.ctrl.T.Helper() 40 | ret := m.ctrl.Call(m, "Bytes") 41 | ret0, _ := ret[0].([]byte) 42 | return ret0 43 | } 44 | 45 | // Bytes indicates an expected call of Bytes. 46 | func (mr *MockPrivateKeyMockRecorder) Bytes() *gomock.Call { 47 | mr.mock.ctrl.T.Helper() 48 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Bytes", reflect.TypeOf((*MockPrivateKey)(nil).Bytes)) 49 | } 50 | 51 | // Kind mocks base method. 52 | func (m *MockPrivateKey) Kind() string { 53 | m.ctrl.T.Helper() 54 | ret := m.ctrl.Call(m, "Kind") 55 | ret0, _ := ret[0].(string) 56 | return ret0 57 | } 58 | 59 | // Kind indicates an expected call of Kind. 60 | func (mr *MockPrivateKeyMockRecorder) Kind() *gomock.Call { 61 | mr.mock.ctrl.T.Helper() 62 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Kind", reflect.TypeOf((*MockPrivateKey)(nil).Kind)) 63 | } 64 | 65 | // PublicKey mocks base method. 66 | func (m *MockPrivateKey) PublicKey() crypto.PublicKey { 67 | m.ctrl.T.Helper() 68 | ret := m.ctrl.Call(m, "PublicKey") 69 | ret0, _ := ret[0].(crypto.PublicKey) 70 | return ret0 71 | } 72 | 73 | // PublicKey indicates an expected call of PublicKey. 74 | func (mr *MockPrivateKeyMockRecorder) PublicKey() *gomock.Call { 75 | mr.mock.ctrl.T.Helper() 76 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PublicKey", reflect.TypeOf((*MockPrivateKey)(nil).PublicKey)) 77 | } 78 | 79 | // Sign mocks base method. 80 | func (m *MockPrivateKey) Sign(message []byte) ([]byte, error) { 81 | m.ctrl.T.Helper() 82 | ret := m.ctrl.Call(m, "Sign", message) 83 | ret0, _ := ret[0].([]byte) 84 | ret1, _ := ret[1].(error) 85 | return ret0, ret1 86 | } 87 | 88 | // Sign indicates an expected call of Sign. 89 | func (mr *MockPrivateKeyMockRecorder) Sign(message interface{}) *gomock.Call { 90 | mr.mock.ctrl.T.Helper() 91 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*MockPrivateKey)(nil).Sign), message) 92 | } 93 | -------------------------------------------------------------------------------- /cipher/aes256cbc/data_test.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-encoding/encodingtest" 7 | ) 8 | 9 | func Test_encryptedData_verify(t *testing.T) { 10 | type fields struct { 11 | InitializationVector []byte 12 | EphemeralPublicKey []byte 13 | Ciphertext []byte 14 | MessageAuthenticationCode []byte 15 | } 16 | tests := []struct { 17 | name string 18 | fields fields 19 | wantErr bool 20 | }{ 21 | { 22 | "success", 23 | fields{ 24 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d16"), 25 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cbadc"), 26 | encodingtest.MustDecodeHex("2c8432ca"), 27 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 28 | }, 29 | false, 30 | }, 31 | { 32 | "err-iv", 33 | fields{ 34 | encodingtest.MustDecodeHex(""), 35 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cbadc"), 36 | encodingtest.MustDecodeHex("2c8432ca"), 37 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 38 | }, 39 | true, 40 | }, 41 | { 42 | "err-ethemeral-pk", 43 | fields{ 44 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d16"), 45 | encodingtest.MustDecodeHex(""), 46 | encodingtest.MustDecodeHex("2c8432ca"), 47 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 48 | }, 49 | true, 50 | }, 51 | { 52 | "err-cipher-text", 53 | fields{ 54 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d16"), 55 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cbadc"), 56 | encodingtest.MustDecodeHex(""), 57 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292"), 58 | }, 59 | true, 60 | }, 61 | { 62 | "err-mac", 63 | fields{ 64 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d16"), 65 | encodingtest.MustDecodeHex("2c8432ca28ce929b86a47f2d40413d161f591f8985229060491573d83f82f292f4dc68f918446332837aa57cd5145235cc40702d962cbb53ac27fb2246fb6cbadc"), 66 | encodingtest.MustDecodeHex("2c8432ca"), 67 | encodingtest.MustDecodeHex(""), 68 | }, 69 | true, 70 | }, 71 | } 72 | for _, tt := range tests { 73 | t.Run(tt.name, func(t *testing.T) { 74 | e := &encryptedData{ 75 | InitializationVector: tt.fields.InitializationVector, 76 | EphemeralPublicKey: tt.fields.EphemeralPublicKey, 77 | Ciphertext: tt.fields.Ciphertext, 78 | MessageAuthenticationCode: tt.fields.MessageAuthenticationCode, 79 | } 80 | if err := e.verify(); (err != nil) != tt.wantErr { 81 | t.Errorf("encryptedData.verify() error = %v, wantErr %v", err, tt.wantErr) 82 | } 83 | }) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /cipher/nacl/serialization.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | "github.com/mailchain/go-crypto/ed25519" 10 | "github.com/mailchain/go-crypto/secp256k1" 11 | "github.com/mailchain/go-crypto/sr25519" 12 | ) 13 | 14 | func pubKeyElements(pubKey crypto.PublicKey) (id byte, data []byte, err error) { 15 | switch pk := pubKey.(type) { 16 | case *secp256k1.PublicKey: 17 | id = crypto.IDSECP256K1 18 | data = pk.Bytes() 19 | case *ed25519.PublicKey: 20 | id = crypto.IDED25519 21 | data = pk.Bytes() 22 | case *sr25519.PublicKey: 23 | id = crypto.IDSR25519 24 | data = pk.Bytes() 25 | default: 26 | err = errors.New("unsupported public key") 27 | } 28 | 29 | return 30 | } 31 | 32 | // serializePublicKeyEncryptedContent encode the encrypted data to the hex format 33 | func serializePublicKeyEncryptedContent(data cipher.EncryptedContent, pubKey crypto.PublicKey) (cipher.EncryptedContent, error) { 34 | pkID, pkBytes, err := pubKeyElements(pubKey) 35 | if err != nil { 36 | return nil, err 37 | } 38 | 39 | pkLen := len(pkBytes) 40 | encodedData := make(cipher.EncryptedContent, 2+len(data)+pkLen) 41 | encodedData[0] = cipher.NACLECDH 42 | encodedData[1] = pkID 43 | copy(encodedData[2:2+pkLen], pkBytes) 44 | copy(encodedData[2+pkLen:], data) 45 | 46 | return encodedData, nil 47 | } 48 | 49 | func serializePrivateKeyEncryptedContent(sealedBox cipher.EncryptedContent, keyID byte) cipher.EncryptedContent { 50 | out := make(cipher.EncryptedContent, 2+len(sealedBox)) 51 | out[0] = cipher.NACLSecretKey 52 | out[1] = byte(keyID) 53 | copy(out[2:], sealedBox) 54 | 55 | return out 56 | } 57 | 58 | // deserializePublicKeyEncryptedContent convert the hex format in to the encrypted data format 59 | func deserializePublicKeyEncryptedContent(raw cipher.EncryptedContent) (cph cipher.EncryptedContent, pubKey crypto.PublicKey, err error) { 60 | if raw[0] != cipher.NACLECDH { 61 | return nil, nil, fmt.Errorf("invalid prefix") 62 | } 63 | 64 | if len(raw) < 35 { 65 | return nil, nil, fmt.Errorf("cipher is too short") // will result in error is less than this 66 | } 67 | 68 | switch raw[1] { 69 | case crypto.IDED25519: 70 | pubKey, err = ed25519.PublicKeyFromBytes(raw[2:34]) 71 | cph = raw[34:] 72 | case crypto.IDSR25519: 73 | pubKey, err = sr25519.PublicKeyFromBytes(raw[2:34]) 74 | cph = raw[34:] 75 | case crypto.IDSECP256K1: 76 | pubKey, err = secp256k1.PublicKeyFromBytes(raw[2:35]) 77 | cph = raw[35:] 78 | default: 79 | return nil, nil, errors.New("unrecognized pubKeyID") 80 | } 81 | 82 | return cph, pubKey, err 83 | } 84 | 85 | // deserializePrivateKeyEncryptedContent convert the hex format in to the encrypted data format 86 | func deserializePrivateKeyEncryptedContent(raw cipher.EncryptedContent) (cph cipher.EncryptedContent, keyID byte, err error) { 87 | if raw[0] != cipher.NACLSecretKey { 88 | return nil, 0x0, fmt.Errorf("invalid prefix") 89 | } 90 | 91 | if len(raw) < 3 { 92 | return nil, 0x0, fmt.Errorf("cipher is too short") // will result in error is less than this 93 | } 94 | 95 | return raw[2:], raw[1], err 96 | } 97 | -------------------------------------------------------------------------------- /cipher/aes256cbc/decrypter.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/subtle" 7 | "errors" 8 | 9 | "github.com/andreburgaud/crypt2go/padding" 10 | "github.com/mailchain/go-crypto" 11 | mc "github.com/mailchain/go-crypto/cipher" 12 | "github.com/mailchain/go-crypto/secp256k1" 13 | ) 14 | 15 | // NewDecrypter create a new decrypter attaching the private key to it 16 | func NewDecrypter(privateKey crypto.PrivateKey) (*Decrypter, error) { 17 | if err := validatePrivateKeyType(privateKey); err != nil { 18 | return nil, err 19 | } 20 | 21 | return &Decrypter{privateKey: privateKey}, nil 22 | } 23 | 24 | // Decrypter will decrypt data using AES256CBC method 25 | type Decrypter struct { 26 | privateKey crypto.PrivateKey 27 | } 28 | 29 | // Decrypt data using recipient private key with AES in CBC mode. 30 | func (d Decrypter) Decrypt(data mc.EncryptedContent) (mc.PlainContent, error) { 31 | encryptedData, err := bytesDecode(data) 32 | if err != nil { 33 | return nil, mc.ErrDecrypt 34 | } 35 | 36 | return decryptEncryptedData(d.privateKey, encryptedData) 37 | } 38 | 39 | func decryptEncryptedData(privKey crypto.PrivateKey, data *encryptedData) ([]byte, error) { 40 | tmpEphemeralPublicKey, err := secp256k1.PublicKeyFromBytes(data.EphemeralPublicKey) 41 | if err != nil { 42 | return nil, mc.ErrDecrypt 43 | } 44 | 45 | ephemeralPublicKey, err := tmpEphemeralPublicKey.(*secp256k1.PublicKey).ECIES() 46 | if err != nil { 47 | return nil, mc.ErrDecrypt 48 | } 49 | 50 | recipientPrivKey, err := asPrivateECIES(privKey) 51 | if err != nil { 52 | return nil, mc.ErrDecrypt 53 | } 54 | 55 | sharedSecret, err := deriveSharedSecret(ephemeralPublicKey, recipientPrivKey) 56 | if err != nil { 57 | return nil, mc.ErrDecrypt 58 | } 59 | 60 | macKey, encryptionKey := generateMacKeyAndEncryptionKey(sharedSecret) 61 | mac, err := generateMac(macKey, data.InitializationVector, *ephemeralPublicKey, data.Ciphertext) 62 | 63 | if err != nil { 64 | return nil, mc.ErrDecrypt 65 | } 66 | 67 | if subtle.ConstantTimeCompare(data.MessageAuthenticationCode, mac) != 1 { 68 | return nil, mc.ErrDecrypt 69 | } 70 | return decryptCBC(encryptionKey, data.InitializationVector, data.Ciphertext) 71 | } 72 | 73 | func decryptCBC(key, iv, ciphertext []byte) ([]byte, error) { 74 | block, err := aes.NewCipher(key) 75 | if err != nil { 76 | return nil, mc.ErrDecrypt 77 | } 78 | 79 | plaintext := make([]byte, len(ciphertext)) 80 | cbc := cipher.NewCBCDecrypter(block, iv) 81 | cbc.CryptBlocks(plaintext, ciphertext) 82 | 83 | plaintext, err = padding.NewPkcs7Padding(block.BlockSize()).Unpad(plaintext) 84 | if err != nil { 85 | return nil, mc.ErrDecrypt 86 | } 87 | 88 | ret := make([]byte, len(plaintext)) 89 | copy(ret, plaintext) 90 | 91 | return ret, nil 92 | } 93 | 94 | func validatePrivateKeyType(privateKey crypto.PrivateKey) error { 95 | switch privateKey.(type) { 96 | case *secp256k1.PrivateKey: 97 | return nil 98 | default: 99 | return errors.New("invalid private key type for aes256cbc decryption") 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ed25519/keys_test.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "github.com/mailchain/go-encoding/encodingtest" 5 | ) 6 | 7 | var ( 8 | aliceSeed = encodingtest.MustDecodeHex("0d9b4a3c10721991c6b806f0f343535dc2b46c74bece50a0a0d6b9f0070d3157") 9 | aliceKeyPair = []byte{0xd, 0x9b, 0x4a, 0x3c, 0x10, 0x72, 0x19, 0x91, 0xc6, 0xb8, 0x6, 0xf0, 0xf3, 0x43, 0x53, 0x5d, 0xc2, 0xb4, 0x6c, 0x74, 0xbe, 0xce, 0x50, 0xa0, 0xa0, 0xd6, 0xb9, 0xf0, 0x7, 0xd, 0x31, 0x57, 0x72, 0x3c, 0xaa, 0x23, 0xa5, 0xb5, 0x11, 0xaf, 0x5a, 0xd7, 0xb7, 0xef, 0x60, 0x76, 0xe4, 0x14, 0xab, 0x7e, 0x75, 0xa9, 0xdc, 0x91, 0xe, 0xa6, 0xe, 0x41, 0x7a, 0x2b, 0x77, 0xa, 0x56, 0x71} 10 | alicePrivateKey = PrivateKey{Key: aliceKeyPair} //nolint: lll 11 | alicePrivateKeyBytes = []byte{0xd, 0x9b, 0x4a, 0x3c, 0x10, 0x72, 0x19, 0x91, 0xc6, 0xb8, 0x6, 0xf0, 0xf3, 0x43, 0x53, 0x5d, 0xc2, 0xb4, 0x6c, 0x74, 0xbe, 0xce, 0x50, 0xa0, 0xa0, 0xd6, 0xb9, 0xf0, 0x7, 0xd, 0x31, 0x57} //nolint: lll 12 | alicePublicKey = PublicKey{Key: []byte{0x72, 0x3c, 0xaa, 0x23, 0xa5, 0xb5, 0x11, 0xaf, 0x5a, 0xd7, 0xb7, 0xef, 0x60, 0x76, 0xe4, 0x14, 0xab, 0x7e, 0x75, 0xa9, 0xdc, 0x91, 0xe, 0xa6, 0xe, 0x41, 0x7a, 0x2b, 0x77, 0xa, 0x56, 0x71}} //nolint: lll 13 | alicePublicKeyBytes = []byte{0x72, 0x3c, 0xaa, 0x23, 0xa5, 0xb5, 0x11, 0xaf, 0x5a, 0xd7, 0xb7, 0xef, 0x60, 0x76, 0xe4, 0x14, 0xab, 0x7e, 0x75, 0xa9, 0xdc, 0x91, 0xe, 0xa6, 0xe, 0x41, 0x7a, 0x2b, 0x77, 0xa, 0x56, 0x71} //nolint: lll 14 | 15 | bobSeed = encodingtest.MustDecodeHex("39d4c97d6a7f9e3306a2b5aae604ee67ec8b1387fffb39128fc055656cff05bb") //nolint: lll 16 | bobKeyPair = []byte{0x39, 0xd4, 0xc9, 0x7d, 0x6a, 0x7f, 0x9e, 0x33, 0x6, 0xa2, 0xb5, 0xaa, 0xe6, 0x4, 0xee, 0x67, 0xec, 0x8b, 0x13, 0x87, 0xff, 0xfb, 0x39, 0x12, 0x8f, 0xc0, 0x55, 0x65, 0x6c, 0xff, 0x5, 0xbb, 0x2e, 0x32, 0x2f, 0x87, 0x40, 0xc6, 0x1, 0x72, 0x11, 0x1a, 0xc8, 0xea, 0xdc, 0xdd, 0xa2, 0x51, 0x2f, 0x90, 0xd0, 0x6d, 0xe, 0x50, 0x3e, 0xf1, 0x89, 0x97, 0x9a, 0x15, 0x9b, 0xec, 0xe1, 0xe8} 17 | bobPrivateKey = PrivateKey{Key: bobKeyPair} //nolint: lll 18 | bobPrivateKeyBytes = []byte{0x39, 0xd4, 0xc9, 0x7d, 0x6a, 0x7f, 0x9e, 0x33, 0x6, 0xa2, 0xb5, 0xaa, 0xe6, 0x4, 0xee, 0x67, 0xec, 0x8b, 0x13, 0x87, 0xff, 0xfb, 0x39, 0x12, 0x8f, 0xc0, 0x55, 0x65, 0x6c, 0xff, 0x5, 0xbb} //nolint: lll 19 | bobPublicKey = PublicKey{Key: []byte{0x2e, 0x32, 0x2f, 0x87, 0x40, 0xc6, 0x1, 0x72, 0x11, 0x1a, 0xc8, 0xea, 0xdc, 0xdd, 0xa2, 0x51, 0x2f, 0x90, 0xd0, 0x6d, 0xe, 0x50, 0x3e, 0xf1, 0x89, 0x97, 0x9a, 0x15, 0x9b, 0xec, 0xe1, 0xe8}} //nolint: lll 20 | bobPublicKeyBytes = []byte{0x2e, 0x32, 0x2f, 0x87, 0x40, 0xc6, 0x1, 0x72, 0x11, 0x1a, 0xc8, 0xea, 0xdc, 0xdd, 0xa2, 0x51, 0x2f, 0x90, 0xd0, 0x6d, 0xe, 0x50, 0x3e, 0xf1, 0x89, 0x97, 0x9a, 0x15, 0x9b, 0xec, 0xe1, 0xe8} //nolint: lll 21 | ) 22 | -------------------------------------------------------------------------------- /cipher/aes256cbc/conversions_test.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1" 9 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 10 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 11 | ) 12 | 13 | func Test_asPrivateECIES(t *testing.T) { 14 | type args struct { 15 | pk crypto.PrivateKey 16 | } 17 | tests := []struct { 18 | name string 19 | args args 20 | wantNil bool 21 | wantErr bool 22 | }{ 23 | { 24 | "success-secp256k1-alice-val", 25 | args{ 26 | func() secp256k1.PrivateKey { 27 | t := secp256k1test.AlicePrivateKey.(*secp256k1.PrivateKey) 28 | return *t 29 | }(), 30 | }, 31 | false, 32 | false, 33 | }, 34 | { 35 | "success-secp256k1-alice-pointer", 36 | args{ 37 | func() *secp256k1.PrivateKey { 38 | return secp256k1test.AlicePrivateKey.(*secp256k1.PrivateKey) 39 | }(), 40 | }, 41 | false, 42 | false, 43 | }, 44 | { 45 | "err-unsupported", 46 | args{ 47 | ed25519test.AlicePrivateKey, 48 | }, 49 | true, 50 | true, 51 | }, 52 | { 53 | "err-unsupported-sr25519", 54 | args{ 55 | sr25519test.AlicePrivateKey, 56 | }, 57 | true, 58 | true, 59 | }, 60 | } 61 | for _, tt := range tests { 62 | t.Run(tt.name, func(t *testing.T) { 63 | got, err := asPrivateECIES(tt.args.pk) 64 | if (err != nil) != tt.wantErr { 65 | t.Errorf("asPrivateECIES() error = %v, wantErr %v", err, tt.wantErr) 66 | return 67 | } 68 | 69 | if (got == nil) != tt.wantNil { 70 | t.Errorf("asPrivateECIES() = %v, wantNil %v", got, tt.wantNil) 71 | } 72 | }) 73 | } 74 | } 75 | 76 | func Test_asPublicECIES(t *testing.T) { 77 | type args struct { 78 | pk crypto.PublicKey 79 | } 80 | tests := []struct { 81 | name string 82 | args args 83 | wantNil bool 84 | wantErr bool 85 | }{ 86 | { 87 | "success-secp256k1-alice-pointer", 88 | args{ 89 | func() crypto.PublicKey { 90 | return secp256k1test.AlicePublicKey.(*secp256k1.PublicKey) 91 | }(), 92 | }, 93 | false, 94 | false, 95 | }, 96 | { 97 | "success-secp256k1-alice-val", 98 | args{ 99 | func() crypto.PublicKey { 100 | pk := secp256k1test.AlicePublicKey.(*secp256k1.PublicKey) 101 | return *pk 102 | }(), 103 | }, 104 | false, 105 | false, 106 | }, 107 | { 108 | "err-invalid", 109 | args{ 110 | ed25519test.AlicePublicKey, 111 | }, 112 | true, 113 | true, 114 | }, 115 | { 116 | "err-invalid-sr25519", 117 | args{ 118 | sr25519test.BobPublicKey, 119 | }, 120 | true, 121 | true, 122 | }, 123 | } 124 | for _, tt := range tests { 125 | t.Run(tt.name, func(t *testing.T) { 126 | got, err := asPublicECIES(tt.args.pk) 127 | if (err != nil) != tt.wantErr { 128 | t.Errorf("asPublicECIES() error = %v, wantErr %v", err, tt.wantErr) 129 | return 130 | } 131 | if (got == nil) != tt.wantNil { 132 | t.Errorf("asPublicECIES() = %v, wantNil %v", got, tt.wantNil) 133 | } 134 | }) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /cipher/nacl/keyexchange_test.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golang/mock/gomock" 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | "github.com/mailchain/go-crypto/cipher/ecdh" 10 | "github.com/mailchain/go-crypto/cryptotest" 11 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 12 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 13 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func Test_getPublicKeyExchange(t *testing.T) { 18 | mockCtrl := gomock.NewController(t) 19 | defer mockCtrl.Finish() 20 | 21 | type args struct { 22 | recipientPublicKey crypto.PublicKey 23 | } 24 | tests := []struct { 25 | name string 26 | args args 27 | want cipher.KeyExchange 28 | wantErr bool 29 | }{ 30 | { 31 | "ed25519", 32 | args{ 33 | ed25519test.AlicePublicKey, 34 | }, 35 | &ecdh.ED25519{}, 36 | false, 37 | }, 38 | { 39 | "sr25519", 40 | args{ 41 | sr25519test.AlicePublicKey, 42 | }, 43 | &ecdh.SR25519{}, 44 | false, 45 | }, 46 | { 47 | "ed25519", 48 | args{ 49 | secp256k1test.AlicePublicKey, 50 | }, 51 | &ecdh.SECP256K1{}, 52 | false, 53 | }, 54 | { 55 | "err-not-supported", 56 | args{ 57 | cryptotest.NewMockPublicKey(mockCtrl), 58 | }, 59 | nil, 60 | true, 61 | }, 62 | } 63 | for _, tt := range tests { 64 | t.Run(tt.name, func(t *testing.T) { 65 | got, err := getPublicKeyExchange(tt.args.recipientPublicKey) 66 | if (err != nil) != tt.wantErr { 67 | t.Errorf("getPublicKeyExchange error = %v, wantErr %v", err, tt.wantErr) 68 | return 69 | } 70 | if !assert.IsType(t, tt.want, got) { 71 | t.Errorf("getPublicKeyExchange = %v, want %v", got, tt.want) 72 | } 73 | }) 74 | } 75 | } 76 | 77 | func Test_getPrivateKeyExchange(t *testing.T) { 78 | mockCtrl := gomock.NewController(t) 79 | defer mockCtrl.Finish() 80 | 81 | type args struct { 82 | recipientPrivateKey crypto.PrivateKey 83 | } 84 | tests := []struct { 85 | name string 86 | args args 87 | want cipher.KeyExchange 88 | wantErr bool 89 | }{ 90 | { 91 | "ed25519", 92 | args{ 93 | ed25519test.AlicePrivateKey, 94 | }, 95 | &ecdh.ED25519{}, 96 | false, 97 | }, 98 | { 99 | "sr25519", 100 | args{ 101 | sr25519test.AlicePrivateKey, 102 | }, 103 | &ecdh.SR25519{}, 104 | false, 105 | }, 106 | { 107 | "ed25519", 108 | args{ 109 | secp256k1test.AlicePrivateKey, 110 | }, 111 | &ecdh.SECP256K1{}, 112 | false, 113 | }, 114 | { 115 | "err-not-supported", 116 | args{ 117 | cryptotest.NewMockPrivateKey(mockCtrl), 118 | }, 119 | nil, 120 | true, 121 | }, 122 | } 123 | for _, tt := range tests { 124 | t.Run(tt.name, func(t *testing.T) { 125 | got, err := getPrivateKeyExchange(tt.args.recipientPrivateKey) 126 | if (err != nil) != tt.wantErr { 127 | t.Errorf("getPrivateKeyExchange error = %v, wantErr %v", err, tt.wantErr) 128 | return 129 | } 130 | if !assert.IsType(t, tt.want, got) { 131 | t.Errorf("getPrivateKeyExchange = %v, want %v", got, tt.want) 132 | } 133 | }) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /cipher/nacl/public_key_end_end_test.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestPublicKeyEncryptDecrypt(t *testing.T) { 14 | cases := []struct { 15 | name string 16 | recipientPublicKey crypto.PublicKey 17 | recipientPrivateKey crypto.PrivateKey 18 | data []byte 19 | err error 20 | }{ 21 | { 22 | "to-alice-short-text", 23 | ed25519test.AlicePublicKey, 24 | ed25519test.AlicePrivateKey, 25 | []byte("Hi Sofia"), 26 | nil, 27 | }, 28 | { 29 | "to-alice-medium-text", 30 | ed25519test.AlicePublicKey, 31 | ed25519test.AlicePrivateKey, 32 | []byte("Hi Sofia, this is a little bit of a longer message to make sure there are no problems"), 33 | nil, 34 | }, 35 | { 36 | "to-bob-short-text", 37 | ed25519test.BobPublicKey, 38 | ed25519test.BobPrivateKey, 39 | []byte("Hi Charlotte"), 40 | nil, 41 | }, 42 | { 43 | "to-bob-medium-text", 44 | ed25519test.BobPublicKey, 45 | ed25519test.BobPrivateKey, 46 | []byte("Hi Charlotte, this is a little bit of a longer message to make sure there are no problems"), 47 | nil, 48 | }, 49 | { 50 | "to-alice-short-text", 51 | sr25519test.AlicePublicKey, 52 | sr25519test.AlicePrivateKey, 53 | []byte("Hi Sofia"), 54 | nil, 55 | }, 56 | { 57 | "to-alice-medium-text", 58 | sr25519test.AlicePublicKey, 59 | sr25519test.AlicePrivateKey, 60 | []byte("Hi Sofia, this is a little bit of a longer message to make sure there are no problems"), 61 | nil, 62 | }, 63 | { 64 | "to-bob-short-text", 65 | sr25519test.BobPublicKey, 66 | sr25519test.BobPrivateKey, 67 | []byte("Hi Charlotte"), 68 | nil, 69 | }, 70 | { 71 | "to-bob-medium-text", 72 | sr25519test.BobPublicKey, 73 | sr25519test.BobPrivateKey, 74 | []byte("Hi Charlotte, this is a little bit of a longer message to make sure there are no problems"), 75 | nil, 76 | }, 77 | { 78 | "to-alice-short-text", 79 | secp256k1test.AlicePublicKey, 80 | secp256k1test.AlicePrivateKey, 81 | []byte("Hi Sofia"), 82 | nil, 83 | }, 84 | { 85 | "to-alice-medium-text", 86 | secp256k1test.AlicePublicKey, 87 | secp256k1test.AlicePrivateKey, 88 | []byte("Hi Sofia, this is a little bit of a longer message to make sure there are no problems"), 89 | nil, 90 | }, 91 | { 92 | "to-bob-short-text", 93 | secp256k1test.BobPublicKey, 94 | secp256k1test.BobPrivateKey, 95 | []byte("Hi Charlotte"), 96 | nil, 97 | }, 98 | { 99 | "to-bob-medium-text", 100 | secp256k1test.BobPublicKey, 101 | secp256k1test.BobPrivateKey, 102 | []byte("Hi Charlotte, this is a little bit of a longer message to make sure there are no problems"), 103 | nil, 104 | }, 105 | } 106 | 107 | for _, tc := range cases { 108 | t.Run(tc.name, func(t *testing.T) { 109 | encrypter, _ := NewPublicKeyEncrypter(tc.recipientPublicKey) 110 | encrypted, err := encrypter.Encrypt(tc.data) 111 | assert.Equal(t, tc.err, err) 112 | assert.NotNil(t, encrypted) 113 | 114 | decrypter, _ := NewPublicKeyDecrypter(tc.recipientPrivateKey) 115 | decrypted, err := decrypter.Decrypt(encrypted) 116 | assert.Equal(t, tc.err, err) 117 | assert.Equal(t, tc.data, []byte(decrypted)) 118 | }) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /secp256r1/private.go: -------------------------------------------------------------------------------- 1 | package secp256r1 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "crypto/rand" 7 | "fmt" 8 | "io" 9 | "math/big" 10 | 11 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 12 | "github.com/mailchain/go-crypto" 13 | ) 14 | 15 | // PrivateKey based on the p256 curve 16 | type PrivateKey struct { 17 | key ecdsa.PrivateKey 18 | rand io.Reader 19 | } 20 | 21 | // Bytes returns the byte representation of the private key 22 | func (pk PrivateKey) Bytes() []byte { 23 | return ethcrypto.FromECDSA(&pk.key) 24 | } 25 | 26 | // Sign signs the message with the private key and returns the signature. 27 | func (pk PrivateKey) Sign(message []byte) (signature []byte, err error) { 28 | r, s, err := ecdsa.Sign(pk.rand, &pk.key, message) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | // normalize 34 | r, s = ecNormalizeSignature(r, s, pk.key.Curve) 35 | // serialize 36 | buf := make([]byte, 64) 37 | r.FillBytes(buf[:32]) 38 | s.FillBytes(buf[32:]) 39 | return buf, nil 40 | } 41 | 42 | // PublicKey return the public key that is derived from the private key 43 | func (pk PrivateKey) PublicKey() crypto.PublicKey { 44 | return &PublicKey{Key: pk.key.PublicKey} 45 | } 46 | 47 | // PrivateKeyFromBytes get a private key from seed []byte 48 | func PrivateKeyFromBytes(privKey []byte) (*PrivateKey, error) { 49 | ecdsaPrivateKey, err := toECDSA(privKey) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return &PrivateKey{key: *ecdsaPrivateKey, rand: rand.Reader}, nil 55 | } 56 | 57 | func GenerateKey(rand io.Reader) (*PrivateKey, error) { 58 | key, err := ecdsa.GenerateKey(elliptic.P256(), rand) 59 | if err != nil { 60 | return nil, err 61 | } 62 | return &PrivateKey{key: *key, rand: rand}, nil 63 | } 64 | 65 | // ecNormalizeSignature ensures strict compliance with the EC spec by returning 66 | // S mod n for the appropriate keys curve. 67 | // 68 | // Details: 69 | // 70 | // Step #6 of the ECDSA algorithm [x] defines an `S` value mod n[0], 71 | // but most signers (OpenSSL, SoftHSM, YubiHSM) don't return a strict modulo. 72 | // This variability was exploited with transaction malleability in Bitcoin, 73 | // leading to BIP#62. BIP#62 Rule #5[1] requires that signatures return a 74 | // strict S = ... mod n which this function forces implemented in btcd here [2] 75 | // [0]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm 76 | // [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#new-rules 77 | // [2]: https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#L49 78 | // 79 | // See also Ecadlabs Signatory: 80 | // https://github.com/ecadlabs/signatory/blob/f57871c2300cb5a53236ea5fcb4f203012b4fe41/pkg/cryptoutils/crypto.go#L17 81 | func ecNormalizeSignature(r, s *big.Int, c elliptic.Curve) (*big.Int, *big.Int) { 82 | r = new(big.Int).Set(r) 83 | s = new(big.Int).Set(s) 84 | 85 | order := c.Params().N 86 | quo := new(big.Int).Quo(order, new(big.Int).SetInt64(2)) 87 | if s.Cmp(quo) > 0 { 88 | s = s.Sub(order, s) 89 | } 90 | return r, s 91 | } 92 | 93 | func toECDSA(pkBytes []byte) (*ecdsa.PrivateKey, error) { 94 | k := new(big.Int).SetBytes(pkBytes) 95 | curveOrder := elliptic.P256().Params().N 96 | if k.Cmp(curveOrder) >= 0 { 97 | return nil, fmt.Errorf("invalid private key for curve Nist P256") 98 | } 99 | 100 | priv := ecdsa.PrivateKey{ 101 | PublicKey: ecdsa.PublicKey{ 102 | Curve: elliptic.P256(), 103 | }, 104 | D: k, 105 | } 106 | 107 | // https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/crypto/ecdsa/ecdsa.go;l=149 108 | priv.PublicKey.X, priv.PublicKey.Y = elliptic.P256().ScalarBaseMult(k.Bytes()) 109 | return &priv, nil 110 | } 111 | -------------------------------------------------------------------------------- /multikey/kind_test.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestGetKeyKindFromBytes(t *testing.T) { 14 | type args struct { 15 | publicKey []byte 16 | privateKey []byte 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | wantKey crypto.PrivateKey 22 | wantErr error 23 | }{ 24 | { 25 | name: "success-ed25519-alice-key", 26 | args: args{ 27 | publicKey: ed25519test.AlicePublicKey.Bytes(), 28 | privateKey: ed25519test.AlicePrivateKey.Bytes(), 29 | }, 30 | wantKey: ed25519test.AlicePrivateKey, 31 | }, 32 | { 33 | name: "success-ed25519-bob-key", 34 | args: args{ 35 | publicKey: ed25519test.BobPublicKey.Bytes(), 36 | privateKey: ed25519test.BobPrivateKey.Bytes(), 37 | }, 38 | wantKey: ed25519test.BobPrivateKey, 39 | }, 40 | { 41 | name: "success-secp256k1-alice-key", 42 | args: args{ 43 | publicKey: secp256k1test.AlicePublicKey.Bytes(), 44 | privateKey: secp256k1test.AlicePrivateKey.Bytes(), 45 | }, 46 | wantKey: secp256k1test.AlicePrivateKey, 47 | }, 48 | { 49 | name: "success-secp256k1-bob-key", 50 | args: args{ 51 | publicKey: secp256k1test.BobPublicKey.Bytes(), 52 | privateKey: secp256k1test.BobPrivateKey.Bytes(), 53 | }, 54 | wantKey: secp256k1test.BobPrivateKey, 55 | }, 56 | { 57 | name: "success-sr25519-alice-key", 58 | args: args{ 59 | publicKey: sr25519test.AlicePublicKey.Bytes(), 60 | privateKey: sr25519test.AlicePrivateKey.Bytes(), 61 | }, 62 | wantKey: sr25519test.AlicePrivateKey, 63 | }, 64 | { 65 | name: "success-sr25519-bob-key", 66 | args: args{ 67 | publicKey: sr25519test.BobPublicKey.Bytes(), 68 | privateKey: sr25519test.BobPrivateKey.Bytes(), 69 | }, 70 | wantKey: sr25519test.BobPrivateKey, 71 | }, 72 | { 73 | name: "err-invalid-public-key-bytes", 74 | args: args{ 75 | publicKey: []byte{0x1}, 76 | privateKey: sr25519test.BobPrivateKey.Bytes(), 77 | }, 78 | wantErr: ErrNoMatch, 79 | }, 80 | { 81 | name: "err-no-key-kinds", 82 | args: args{ 83 | publicKey: []byte{0x1}, 84 | privateKey: []byte{0x2}, 85 | }, 86 | wantErr: ErrNoMatch, 87 | }, 88 | } 89 | 90 | for _, tt := range tests { 91 | t.Run(tt.name, func(t *testing.T) { 92 | privateKey, err := GetKeyKindFromBytes(tt.args.publicKey, tt.args.privateKey) 93 | 94 | if !(err == tt.wantErr) { 95 | t.Errorf("GetKeyKindFromBytes() err = %v, want %v", err, tt.wantErr) 96 | } 97 | 98 | if !assert.Equal(t, privateKey, tt.wantKey) { 99 | t.Errorf("GetKeyKindFromBytes() key = %v, want %v", privateKey, tt.wantKey) 100 | } 101 | }) 102 | } 103 | } 104 | 105 | func TestRemoveDuplicates(t *testing.T) { 106 | tests := []struct { 107 | in []string 108 | want []string 109 | }{ 110 | { 111 | []string{"x", "x", "y"}, 112 | []string{"x", "y"}, 113 | }, 114 | { 115 | nil, 116 | nil, 117 | }, 118 | { 119 | []string{}, 120 | []string{}, 121 | }, 122 | { 123 | []string{"c", "a", "c"}, 124 | []string{"c", "a"}, 125 | }, 126 | { 127 | []string{"d", "a", "f", "a", "e", "e", "z", "f", "t"}, 128 | []string{"d", "a", "f", "e", "z", "t"}, 129 | }, 130 | } 131 | for _, tt := range tests { 132 | t.Run("remove-duplicates", func(t *testing.T) { 133 | got := removeDuplicates(tt.in) 134 | if !assert.Equal(t, tt.want, got) { 135 | t.Errorf("removeDuplicates() = %v, want %v", got, tt.want) 136 | } 137 | }) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /chaincode/generate_test.go: -------------------------------------------------------------------------------- 1 | package chaincode 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-encoding/encodingtest" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestChainCodeFromDeriveIndexBytes(t *testing.T) { 11 | type args struct { 12 | input []byte 13 | } 14 | tests := []struct { 15 | name string 16 | args args 17 | want []byte 18 | }{ 19 | { 20 | "0", 21 | args{ 22 | encodingtest.MustDecodeHex("0000000000000000000000000000000000000000000000000000000000000000"), 23 | }, 24 | []byte{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 25 | }, 26 | { 27 | "16-FF", 28 | args{ 29 | encodingtest.MustDecodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 30 | }, 31 | []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 32 | }, 33 | { 34 | "32-FF", 35 | args{ 36 | encodingtest.MustDecodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 37 | }, 38 | []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, 39 | }, 40 | { 41 | "64-FF", 42 | args{ 43 | encodingtest.MustDecodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), 44 | }, 45 | []byte{0x41, 0xdb, 0x9, 0x6e, 0x15, 0xf0, 0x3b, 0x13, 0x5b, 0x4, 0xe9, 0x9e, 0x84, 0x8e, 0xf, 0x76, 0xcb, 0x37, 0x39, 0xc3, 0x5f, 0xfe, 0x7, 0xe3, 0x67, 0x9d, 0xf3, 0x78, 0x67, 0xbc, 0xb5, 0x73}, 46 | }, 47 | } 48 | for _, tt := range tests { 49 | t.Run(tt.name, func(t *testing.T) { 50 | assert.Equal(t, tt.want, ChainCodeFromDeriveIndexBytes(tt.args.input)) 51 | }) 52 | } 53 | } 54 | 55 | func TestChainCodeFromDeriveIndexUint64(t *testing.T) { 56 | type args struct { 57 | input uint64 58 | } 59 | tests := []struct { 60 | name string 61 | args args 62 | want []byte 63 | }{ 64 | { 65 | "10", 66 | args{ 67 | 10, 68 | }, 69 | []byte{0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 70 | }, 71 | { 72 | "1000", 73 | args{ 74 | 1000, 75 | }, 76 | []byte{0xe8, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 77 | }, 78 | } 79 | for _, tt := range tests { 80 | t.Run(tt.name, func(t *testing.T) { 81 | assert.Equal(t, tt.want, ChainCodeFromDeriveIndexUint64(tt.args.input)) 82 | }) 83 | } 84 | } 85 | 86 | func TestChainCodeFromDeriveIndexString(t *testing.T) { 87 | type args struct { 88 | input string 89 | } 90 | tests := []struct { 91 | name string 92 | args args 93 | want []byte 94 | }{ 95 | { 96 | "short", 97 | args{ 98 | "short", 99 | }, 100 | encodingtest.MustDecodeHex("1473686f72740000000000000000000000000000000000000000000000000000"), 101 | }, 102 | { 103 | "short-string", 104 | args{ 105 | "short-string", 106 | }, 107 | encodingtest.MustDecodeHex("3073686f72742d737472696e6700000000000000000000000000000000000000"), 108 | }, 109 | { 110 | "long-string", 111 | args{ 112 | "string longer than chain code length of 32 bytes", 113 | }, 114 | encodingtest.MustDecodeHex("79a03475da2fcd8d43e22be1f0f15946f171571506008baa381fecc84373ddea"), 115 | }, 116 | } 117 | for _, tt := range tests { 118 | t.Run(tt.name, func(t *testing.T) { 119 | assert.Equal(t, tt.want, ChainCodeFromDeriveIndexString(tt.args.input)) 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /encoding/messaging_test.go: -------------------------------------------------------------------------------- 1 | package keys 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | "github.com/mailchain/go-encoding" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestPrefixMsgKey(t *testing.T) { 15 | type args struct { 16 | key []byte 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | len int 22 | want string 23 | }{ 24 | { 25 | "min-32-bytes-key", 26 | args{[]byte{ 27 | 0x0, 28 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 29 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 30 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 31 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 32 | }}, 33 | 33, 34 | "MsgKey12E6xyuFqQBvKyzJHMa4y6qiEwNw94fosXXr1YA5gVTJkeB", 35 | }, 36 | { 37 | "min-plus-one-32-bytes-key", 38 | args{[]byte{ 39 | 0x0, 40 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 41 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 42 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 43 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 44 | }}, 45 | 33, 46 | "MsgKey12E6xyuFqQBvKyzJHMa4y6qiEwNw94fosXXr1YA5gVTJkeC", 47 | }, 48 | { 49 | "mid-32-bytes", 50 | args{[]byte{ 51 | 0x88, 52 | 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 53 | 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 54 | 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 55 | 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 56 | }}, 57 | 33, 58 | "MsgKey12vfP2WE4yAKJfJqosQHTc7bJz47uBzipVucRud4efK9byq", 59 | }, 60 | { 61 | "max-32-bytes", 62 | args{[]byte{ 63 | 0xFF, 64 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 65 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 66 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 67 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 68 | }}, 69 | 33, 70 | "MsgKey13Y9k4nKmqmAQkiKPpWDtYjEkGtuZoQX9bjf3jQRVowWben", 71 | }, 72 | } 73 | for _, tt := range tests { 74 | t.Run(tt.name, func(t *testing.T) { 75 | assert.Equal(t, tt.len, len(tt.args.key)) 76 | 77 | assert.Equal(t, tt.want, encoding.EncodeBase58(append(PrefixMsgKey, tt.args.key...))) 78 | }) 79 | } 80 | } 81 | 82 | func TestEncodeMessagingPublicKey(t *testing.T) { 83 | type args struct { 84 | key crypto.PublicKey 85 | } 86 | tests := []struct { 87 | name string 88 | args args 89 | want string 90 | assertion assert.ErrorAssertionFunc 91 | }{ 92 | { 93 | "secp256k1-alice", 94 | args{ 95 | secp256k1test.AlicePublicKey, 96 | }, 97 | "", 98 | assert.Error, 99 | }, 100 | { 101 | "secp256k1-bob", 102 | args{ 103 | secp256k1test.BobPublicKey, 104 | }, 105 | "", 106 | assert.Error, 107 | }, 108 | { 109 | "ed25519-alice", 110 | args{ 111 | ed25519test.AlicePublicKey, 112 | }, 113 | "MsgKey13PNYVnxBhux7pay5k6TBKrhHasBWAavReMXZLJapZfm3je", 114 | assert.NoError, 115 | }, 116 | { 117 | "ed25519-bob", 118 | args{ 119 | ed25519test.BobPublicKey, 120 | }, 121 | "MsgKey13PHxtoUBTupmWxMiyDYABwbME5gYSwZhDxBAUYYfeQouDZ", 122 | assert.NoError, 123 | }, 124 | { 125 | "sr25519-alice", 126 | args{ 127 | sr25519test.AlicePublicKey, 128 | }, 129 | "MsgKey13PZc7G9tocExwkoMD74B8pgnyetWRoM7cnbAj4y3qHfsxX", 130 | assert.NoError, 131 | }, 132 | { 133 | "sr25519-bob", 134 | args{ 135 | sr25519test.BobPublicKey, 136 | }, 137 | "MsgKey13PgzejjbLukR2MKBRJ2LUMsnFojTzxeKqS6jQXVr3yndh9", 138 | assert.NoError, 139 | }, 140 | } 141 | for _, tt := range tests { 142 | t.Run(tt.name, func(t *testing.T) { 143 | got, err := EncodeMessagingPublicKey(tt.args.key) 144 | tt.assertion(t, err) 145 | assert.Equal(t, tt.want, got) 146 | }) 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /sr25519/private.go: -------------------------------------------------------------------------------- 1 | package sr25519 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | 7 | "github.com/gtank/merlin" 8 | "github.com/gtank/ristretto255" 9 | "github.com/mailchain/go-crypto" 10 | "github.com/mailchain/go-crypto/internal/schnorrkel" 11 | ) 12 | 13 | const ( 14 | seedSize = 32 15 | ) 16 | 17 | func GenerateKey(rand io.Reader) (*PrivateKey, error) { 18 | seed := make([]byte, seedSize) 19 | if _, err := io.ReadFull(rand, seed); err != nil { 20 | return nil, err 21 | } 22 | 23 | return PrivateKeyFromBytes(seed) 24 | } 25 | 26 | // PrivateKey based on the sr25519 curve 27 | type PrivateKey struct { 28 | secretKey schnorrkel.SecretKey 29 | } 30 | 31 | // Bytes returns the byte representation of the private key 32 | func (pk PrivateKey) Bytes() []byte { 33 | return pk.secretKey.Seed() 34 | } 35 | 36 | // PublicKey return the crypto.PublicKey that is derived from the Privatekey 37 | func (pk PrivateKey) PublicKey() crypto.PublicKey { 38 | key := ristretto255.NewScalar() 39 | if err := key.Decode(pk.secretKey.Key()); err != nil { 40 | return nil 41 | } 42 | 43 | return &PublicKey{key: ristretto255.NewElement().ScalarBaseMult(key).Encode([]byte{})} 44 | } 45 | 46 | // Sign uses the PrivateKey to sign the message using the sr25519 signature algorithm 47 | func (pk PrivateKey) Sign(message []byte) ([]byte, error) { 48 | context := newSigningContext(substrateContext, message) 49 | 50 | context.AppendMessage([]byte("proto-name"), []byte("Schnorr-sig")) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L173 51 | context.AppendMessage([]byte("sign:pk"), pk.PublicKey().Bytes()) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L174 52 | 53 | r, err := witness(pk.secretKey.Nonce()) // witness_scalar Not implemented https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L176 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | R := ristretto255.NewElement().ScalarBaseMult(r) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L177 59 | context.AppendMessage([]byte("sign:R"), R.Encode([]byte{})) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L179 60 | 61 | k := context.challengeScalar([]byte("sign:c")) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L181 62 | 63 | pkScalar := ristretto255.NewScalar() 64 | if err := pkScalar.Decode(pk.secretKey.Key()); err != nil { 65 | return nil, err 66 | } 67 | 68 | s := pkScalar.Multiply(pkScalar, k).Add(pkScalar, r) // https://github.com/w3f/schnorrkel/blob/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src/sign.rs#L182 69 | sig := signature{R: R, S: s} 70 | 71 | return sig.Encode(), nil 72 | } 73 | 74 | // PrivateKeyFromBytes get a private key from seed []byte 75 | func PrivateKeyFromBytes(privKey []byte) (*PrivateKey, error) { 76 | switch len(privKey) { 77 | case seedSize: 78 | seed := [seedSize]byte{} 79 | copy(seed[:], privKey) 80 | 81 | return &PrivateKey{secretKey: schnorrkel.NewSecretKeySR25519(seed)}, nil 82 | default: 83 | return nil, fmt.Errorf("sr25519: bad key length") 84 | } 85 | } 86 | 87 | func ExchangeKeys(privKey *PrivateKey, pubKey *PublicKey, length int) ([]byte, error) { 88 | // https://github.com/w3f/schnorrkel/tree/4112f6e8cb684a1cc6574f9097497e1e302ab9a8/src 89 | transcript := merlin.NewTranscript("KEX") 90 | transcript.AppendMessage([]byte("ctx"), []byte{}) 91 | privKey.secretKey.Key() 92 | 93 | a := ristretto255.NewElement() 94 | if err := a.Decode(pubKey.key); err != nil { 95 | return []byte{}, err 96 | } 97 | 98 | pkScalar := ristretto255.NewScalar() 99 | if err := pkScalar.Decode(privKey.secretKey.Key()); err != nil { 100 | return []byte{}, err 101 | } 102 | 103 | a.ScalarMult(pkScalar, a) 104 | transcript.AppendMessage([]byte{}, a.Encode([]byte{})) 105 | 106 | return transcript.ExtractBytes([]byte{}, length), nil 107 | } 108 | -------------------------------------------------------------------------------- /ed25519/public_test.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPublicKey_Bytes(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | pk PublicKey 14 | want []byte 15 | }{ 16 | { 17 | "alice", 18 | alicePublicKey, 19 | alicePublicKeyBytes, 20 | }, 21 | { 22 | "bob", 23 | bobPublicKey, 24 | bobPublicKeyBytes, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | if got := tt.pk.Bytes(); !reflect.DeepEqual(got, tt.want) { 30 | t.Errorf("PublicKey.Bytes() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestPublicKeyFromBytes(t *testing.T) { 37 | type args struct { 38 | keyBytes []byte 39 | } 40 | tests := []struct { 41 | name string 42 | args args 43 | want *PublicKey 44 | wantErr bool 45 | }{ 46 | { 47 | "alice", 48 | args{ 49 | alicePublicKeyBytes, 50 | }, 51 | &alicePublicKey, 52 | false, 53 | }, 54 | { 55 | "err-too-short", 56 | args{ 57 | []byte{0x72, 0x3c, 0xaa, 0x23}, 58 | }, 59 | nil, 60 | true, 61 | }, 62 | } 63 | for _, tt := range tests { 64 | t.Run(tt.name, func(t *testing.T) { 65 | got, err := PublicKeyFromBytes(tt.args.keyBytes) 66 | if (err != nil) != tt.wantErr { 67 | t.Errorf("PublicKeyFromBytes() error = %v, wantErr %v", err, tt.wantErr) 68 | return 69 | } 70 | if !assert.Equal(t, tt.want, got) { 71 | t.Errorf("PublicKeyFromBytes() = %v, want %v", got, tt.want) 72 | } 73 | }) 74 | } 75 | } 76 | 77 | func TestPublicKey_Verify(t *testing.T) { 78 | tests := []struct { 79 | name string 80 | pk PublicKey 81 | message []byte 82 | sig []byte 83 | want bool 84 | }{ 85 | { 86 | "success-bob", 87 | bobPublicKey, 88 | []byte("message"), 89 | []byte{0x7d, 0x51, 0xea, 0xfa, 0x52, 0x78, 0x31, 0x69, 0xd0, 0xa9, 0x4a, 0xc, 0x9f, 0x2b, 0xca, 0xd5, 0xe0, 0x3d, 0x29, 0x17, 0x33, 0x0, 0x93, 0xf, 0xf3, 0xc7, 0xd6, 0x3b, 0xfd, 0x64, 0x17, 0xae, 0x1b, 0xc8, 0x1f, 0xef, 0x51, 0xba, 0x14, 0x9a, 0xe8, 0xa1, 0xe1, 0xda, 0xe0, 0x5f, 0xdc, 0xa5, 0x7, 0x8b, 0x14, 0xba, 0xc4, 0xcf, 0x26, 0xcc, 0xc6, 0x1, 0x1e, 0x5e, 0xab, 0x77, 0x3, 0xc}, 90 | true, 91 | }, 92 | { 93 | "success-alice", 94 | alicePublicKey, 95 | []byte("egassem"), 96 | []byte{0xde, 0x6c, 0x88, 0xe6, 0x9c, 0x9f, 0x93, 0xb, 0x59, 0xdd, 0xf4, 0x80, 0xc2, 0x9a, 0x55, 0x79, 0xec, 0x89, 0x5c, 0xa9, 0x7a, 0x36, 0xf6, 0x69, 0x74, 0xc1, 0xf0, 0x15, 0x5c, 0xc0, 0x66, 0x75, 0x2e, 0xcd, 0x9a, 0x9b, 0x41, 0x35, 0xd2, 0x72, 0x32, 0xe0, 0x54, 0x80, 0xbc, 0x98, 0x58, 0x1, 0xa9, 0xfd, 0xe4, 0x27, 0xc7, 0xef, 0xa5, 0x42, 0x5f, 0xf, 0x46, 0x49, 0xb8, 0xad, 0xbd, 0x5}, 97 | true, 98 | }, 99 | { 100 | "err-invalid-signature-bob", 101 | bobPublicKey, 102 | []byte("message"), 103 | []byte{0x2e, 0x11, 0x79, 0x88, 0xb7, 0xc, 0x44, 0xac, 0xdb, 0xe0, 0x27, 0x2a, 0x30, 0x7b, 0x42, 0xf, 0x21, 0xe, 0x5a, 0x79, 0x37, 0x2b, 0x5a, 0xf4, 0x3d, 0x6a, 0x5, 0xf9, 0xab, 0xa, 0x83, 0x4, 0x99, 0x95, 0x5c, 0xc8, 0x98, 0x4, 0xeb, 0x21, 0x4, 0x14, 0x95, 0x1b, 0x79, 0xbc, 0x67, 0xa6, 0x4, 0x66, 0xc9, 0xa4, 0xec, 0xc0, 0xc2, 0x42, 0x51, 0x38, 0xee, 0x29, 0xe9, 0x54, 0x2b, 0x3}, 104 | false, 105 | }, 106 | { 107 | "err-invalid-signature-alice", 108 | alicePublicKey, 109 | []byte("egassem"), 110 | []byte{0x3, 0x5e, 0x29, 0xa9, 0xd8, 0xb3, 0x6c, 0xb8, 0x92, 0x8, 0xc9, 0x45, 0x15, 0xf9, 0x7c, 0x4d, 0x56, 0x7c, 0x36, 0x84, 0x34, 0x2, 0xcc, 0x51, 0xa2, 0x45, 0x5a, 0x39, 0xce, 0x11, 0xf2, 0x7, 0x33, 0xa7, 0xe2, 0x37, 0x9c, 0x8, 0xa4, 0x60, 0x1a, 0x23, 0x7e, 0x24, 0xb5, 0x49, 0x67, 0xf0, 0x8b, 0x78, 0xea, 0x28, 0x97, 0xe, 0x2, 0xb1, 0xaa, 0x76, 0x8b, 0x6b, 0xfc, 0xa2, 0x60, 0xc}, 111 | false, 112 | }, 113 | } 114 | for _, tt := range tests { 115 | t.Run(tt.name, func(t *testing.T) { 116 | got := tt.pk.Verify(tt.message, tt.sig) 117 | if !assert.Equal(t, tt.want, got) { 118 | t.Errorf("PublicKey.Verify() = %v, want %v", got, tt.want) 119 | } 120 | }) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /cipher/nacl/private_key_decrypter_test.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | "github.com/mailchain/go-crypto/cipher/ecdh" 10 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 11 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 12 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 13 | "github.com/mailchain/go-encoding/encodingtest" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestPrivateKeyDecrypter_Decrypt(t *testing.T) { 18 | type fields struct { 19 | privateKey crypto.PrivateKey 20 | keyExchange cipher.KeyExchange 21 | } 22 | type args struct { 23 | data cipher.EncryptedContent 24 | } 25 | tests := []struct { 26 | name string 27 | fields fields 28 | args args 29 | want cipher.PlainContent 30 | assertion assert.ErrorAssertionFunc 31 | }{ 32 | { 33 | "secp256k1-alice", 34 | fields{ 35 | secp256k1test.AlicePrivateKey, 36 | func() cipher.KeyExchange { 37 | k, _ := ecdh.NewSECP256K1(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 38 | return k 39 | }(), 40 | }, 41 | args{ 42 | encodingtest.MustDecodeHex("2be14142434445464748494a4b4c4d4e4f5051525354555657585ff8026ea550c27f5ec06e3ecdfb0850f3352400b7e9e2"), 43 | }, 44 | []byte("message"), 45 | assert.NoError, 46 | }, 47 | { 48 | "secp256k1-bob", 49 | fields{ 50 | secp256k1test.BobPrivateKey, 51 | func() cipher.KeyExchange { 52 | k, _ := ecdh.NewSECP256K1(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 53 | return k 54 | }(), 55 | }, 56 | args{ 57 | encodingtest.MustDecodeHex("2be14142434445464748494a4b4c4d4e4f5051525354555657583abb4c6b03073d8318a8edfa5e3820d761b9d07682e179"), 58 | }, 59 | []byte("message"), 60 | assert.NoError, 61 | }, 62 | { 63 | "ed25519-alice", 64 | fields{ 65 | ed25519test.AlicePrivateKey, 66 | func() cipher.KeyExchange { 67 | k, _ := ecdh.NewED25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 68 | return k 69 | }(), 70 | }, 71 | args{ 72 | encodingtest.MustDecodeHex("2be24142434445464748494a4b4c4d4e4f505152535455565758ede31931c34d9e1d251cf6466b1d628957a55bcce73486"), 73 | }, 74 | []byte("message"), 75 | assert.NoError, 76 | }, 77 | { 78 | "ed25519-bob", 79 | fields{ 80 | ed25519test.BobPrivateKey, 81 | func() cipher.KeyExchange { 82 | k, _ := ecdh.NewED25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 83 | return k 84 | }(), 85 | }, 86 | args{ 87 | encodingtest.MustDecodeHex("2be24142434445464748494a4b4c4d4e4f5051525354555657581a7d53c9fc1d9b4103f7e9c234f5897688cc68dbadbe17"), 88 | }, 89 | []byte("message"), 90 | assert.NoError, 91 | }, 92 | { 93 | "sr25519-alice", 94 | fields{ 95 | sr25519test.AlicePrivateKey, 96 | func() cipher.KeyExchange { 97 | k, _ := ecdh.NewSR25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 98 | return k 99 | }(), 100 | }, 101 | args{ 102 | encodingtest.MustDecodeHex("2be24142434445464748494a4b4c4d4e4f505152535455565758ede31931c34d9e1d251cf6466b1d628957a55bcce73486"), 103 | }, 104 | nil, 105 | assert.Error, 106 | }, 107 | { 108 | "sr25519-bob", 109 | fields{ 110 | sr25519test.BobPrivateKey, 111 | func() cipher.KeyExchange { 112 | k, _ := ecdh.NewSR25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 113 | return k 114 | }(), 115 | }, 116 | args{ 117 | encodingtest.MustDecodeHex("2be24142434445464748494a4b4c4d4e4f5051525354555657581a7d53c9fc1d9b4103f7e9c234f5897688cc68dbadbe17"), 118 | }, 119 | nil, 120 | assert.Error, 121 | }, 122 | } 123 | for _, tt := range tests { 124 | t.Run(tt.name, func(t *testing.T) { 125 | d := PrivateKeyDecrypter{ 126 | privateKey: tt.fields.privateKey, 127 | keyExchange: tt.fields.keyExchange, 128 | } 129 | got, err := d.Decrypt(tt.args.data) 130 | tt.assertion(t, err) 131 | assert.Equal(t, tt.want, got) 132 | }) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /sr25519/public_test.go: -------------------------------------------------------------------------------- 1 | package sr25519 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPublicKey_Bytes(t *testing.T) { 11 | tests := []struct { 12 | name string 13 | pk PublicKey 14 | want []byte 15 | }{ 16 | { 17 | "alice", 18 | alicePublicKey, 19 | alicePublicKeyBytes, 20 | }, 21 | { 22 | "bob", 23 | bobPublicKey, 24 | bobPublicKeyBytes, 25 | }, 26 | } 27 | for _, tt := range tests { 28 | t.Run(tt.name, func(t *testing.T) { 29 | if got := tt.pk.Bytes(); !assert.Equal(t, tt.want, got) { 30 | t.Errorf("PublicKey.Bytes() = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | 36 | func TestPublicKeyFromBytes(t *testing.T) { 37 | type args struct { 38 | keyBytes []byte 39 | } 40 | tests := []struct { 41 | name string 42 | args args 43 | want crypto.PublicKey 44 | wantErr bool 45 | }{ 46 | { 47 | "success-alice-bytes", 48 | args{ 49 | alicePublicKeyBytes, 50 | }, 51 | &alicePublicKey, 52 | false, 53 | }, 54 | { 55 | "success-bob-bytes", 56 | args{ 57 | bobPublicKeyBytes, 58 | }, 59 | &bobPublicKey, 60 | false, 61 | }, 62 | { 63 | "err-too-short", 64 | args{ 65 | []byte{0x72, 0x3c, 0xaa, 0x23}, 66 | }, 67 | nil, 68 | true, 69 | }, 70 | } 71 | for _, tt := range tests { 72 | t.Run(tt.name, func(t *testing.T) { 73 | got, err := PublicKeyFromBytes(tt.args.keyBytes) 74 | if (err != nil) != tt.wantErr { 75 | t.Errorf("PublicKeyFromBytes() error = %v, wantErr %v", err, tt.wantErr) 76 | return 77 | } 78 | if !assert.Equal(t, tt.want, got) { 79 | t.Errorf("PublicKeyFromBytes() = %v, want %v", got, tt.want) 80 | } 81 | }) 82 | } 83 | } 84 | 85 | func TestPublicKey_Verify(t *testing.T) { 86 | tests := []struct { 87 | name string 88 | pk PublicKey 89 | message []byte 90 | sig []byte 91 | want bool 92 | }{ 93 | { 94 | "success-bob", 95 | bobPublicKey, 96 | []byte("message"), 97 | []byte{0x62, 0x51, 0xaa, 0x51, 0xa4, 0xb4, 0x15, 0xa3, 0xfa, 0x28, 0x86, 0xa4, 0xc6, 0x74, 0xd7, 0x47, 0xf9, 0x1d, 0x27, 0x33, 0xf5, 0xf2, 0x01, 0x00, 0x11, 0x35, 0x1c, 0x7c, 0x79, 0x1a, 0x06, 0x28, 0xca, 0x2d, 0xa1, 0xab, 0xa2, 0x27, 0x34, 0xfe, 0x80, 0x23, 0xe2, 0x9c, 0x87, 0xb6, 0xda, 0xa3, 0x12, 0xf0, 0xc2, 0xef, 0x3e, 0x56, 0x1b, 0xac, 0x48, 0xc6, 0x2a, 0xc5, 0xe6, 0xcd, 0x85, 0x80}, 98 | true, 99 | }, 100 | { 101 | "success-alice", 102 | alicePublicKey, 103 | []byte("egassem"), 104 | []byte{0x56, 0x61, 0x62, 0x9c, 0x9b, 0x2f, 0xd6, 0xff, 0x80, 0xb4, 0x05, 0x35, 0x5e, 0xf4, 0x12, 0xa5, 0xc5, 0xaa, 0xfe, 0xe4, 0x29, 0x7d, 0x34, 0x11, 0x84, 0x2d, 0xfa, 0x2c, 0x76, 0xbc, 0x1b, 0x74, 0x61, 0x4b, 0xc6, 0x6b, 0xc2, 0x61, 0xa5, 0x65, 0xdc, 0x2b, 0x08, 0x44, 0x64, 0x3b, 0x72, 0xd4, 0x2f, 0xbe, 0xde, 0x7e, 0xc9, 0x80, 0xe3, 0xd9, 0x35, 0x7f, 0x37, 0x0d, 0xd3, 0x42, 0xe7, 0x83}, 105 | true, 106 | }, 107 | { 108 | "err-invalid-signature-bob", 109 | bobPublicKey, 110 | []byte("message"), 111 | []byte{0xde, 0x6c, 0x88, 0xe6, 0x9c, 0x9f, 0x93, 0xb, 0x59, 0xdd, 0xf4, 0x80, 0xc2, 0x9a, 0x55, 0x79, 0xec, 0x89, 0x5c, 0xa9, 0x7a, 0x36, 0xf6, 0x69, 0x74, 0xc1, 0xf0, 0x15, 0x5c, 0xc0, 0x66, 0x75, 0x2e, 0xcd, 0x9a, 0x9b, 0x41, 0x35, 0xd2, 0x72, 0x32, 0xe0, 0x54, 0x80, 0xbc, 0x98, 0x58, 0x1, 0xa9, 0xfd, 0xe4, 0x27, 0xc7, 0xef, 0xa5, 0x42, 0x5f, 0xf, 0x46, 0x49, 0xb8, 0xad, 0xbd, 0x5}, 112 | false, 113 | }, 114 | { 115 | "err-invalid-signature-alice", 116 | alicePublicKey, 117 | []byte("egassem"), 118 | []byte{0x7d, 0x51, 0xea, 0xfa, 0x52, 0x78, 0x31, 0x69, 0xd0, 0xa9, 0x4a, 0xc, 0x9f, 0x2b, 0xca, 0xd5, 0xe0, 0x3d, 0x29, 0x17, 0x33, 0x0, 0x93, 0xf, 0xf3, 0xc7, 0xd6, 0x3b, 0xfd, 0x64, 0x17, 0xae, 0x1b, 0xc8, 0x1f, 0xef, 0x51, 0xba, 0x14, 0x9a, 0xe8, 0xa1, 0xe1, 0xda, 0xe0, 0x5f, 0xdc, 0xa5, 0x7, 0x8b, 0x14, 0xba, 0xc4, 0xcf, 0x26, 0xcc, 0xc6, 0x1, 0x1e, 0x5e, 0xab, 0x77, 0x3, 0xc}, 119 | false, 120 | }, 121 | } 122 | for _, tt := range tests { 123 | t.Run(tt.name, func(t *testing.T) { 124 | got := tt.pk.Verify(tt.message, tt.sig) 125 | if !assert.Equal(t, tt.want, got) { 126 | t.Errorf("PublicKey.Verify() = %v, want %v", got, tt.want) 127 | } 128 | }) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /secp256r1/public_test.go: -------------------------------------------------------------------------------- 1 | package secp256r1 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-encoding/encodingtest" 8 | "github.com/stretchr/testify/assert" 9 | "golang.org/x/crypto/blake2b" 10 | ) 11 | 12 | func TestPublicKey_Bytes(t *testing.T) { 13 | type args struct { 14 | key *PublicKey 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want []byte 20 | }{ 21 | { 22 | "alice", 23 | args{ 24 | key: &aliceSECP256R1PublicKey, 25 | }, 26 | aliceSECP256R1PublicKeyBytes, 27 | }, 28 | { 29 | "bob", 30 | args{ 31 | key: &bobSECP256R1PublicKey, 32 | }, 33 | bobSECP256R1PublicKeyBytes, 34 | }, 35 | { 36 | "carlos", 37 | args{ 38 | key: &carlosSECP256R1PublicKey, 39 | }, 40 | carlosSECP256R1PublicKeyBytes, 41 | }, 42 | } 43 | for _, tt := range tests { 44 | t.Run(tt.name, func(t *testing.T) { 45 | assert.Equal(t, tt.args.key.Bytes(), tt.want) 46 | }) 47 | } 48 | } 49 | 50 | func TestPublicKey_Verify(t *testing.T) { 51 | type args struct { 52 | message []byte 53 | sig []byte 54 | } 55 | tests := []struct { 56 | name string 57 | target PublicKey 58 | args args 59 | want bool 60 | }{ 61 | { 62 | "alice-success", 63 | aliceSECP256R1PublicKey, 64 | args{ 65 | message: []byte("hello from mailchain"), 66 | sig: encodingtest.MustDecodeHex("ba1618aca63b24376e6f538beee4f757523081306ed10f301ada1a5919a5b68d526b2ae2f1cd8fd6985c42dd0fe681dc1d7e2ea091bc0c2e524de6d65f2c7318"), 67 | }, 68 | true, 69 | }, 70 | { 71 | "alice-success-ts-compatibility", 72 | aliceSECP256R1PublicKey, 73 | args{ 74 | message: []byte("hello from mailchain"), 75 | sig: encodingtest.MustDecodeHex("3eb824015c1d13541ff6b6a5af1e64a7aa1d2e5fdc17c935a37d766616c307634eb97a751000158a88118f8ccfd43e9dd0eece2dcbdc0368b365fb8d51bf1d6c"), 76 | }, 77 | true, 78 | }, 79 | { 80 | "bob-success", 81 | bobSECP256R1PublicKey, 82 | args{ 83 | message: []byte("hello from mailchain"), 84 | sig: encodingtest.MustDecodeHex("f3c5e4a924b18264c166d74f3210a1fbc42dd4d93b0d1b0f6a96ef6584ae31905d698badee1399287f4c3f49bca4a690a19ef95ce620aecc862cc1e4f8d0cdf1"), 85 | }, 86 | true, 87 | }, 88 | { 89 | "bob-success-ts-compatibility", 90 | bobSECP256R1PublicKey, 91 | args{ 92 | message: []byte("hello from mailchain"), 93 | sig: encodingtest.MustDecodeHex("4b5c334e445a2b854b55918cde2be2b5a9ee4347bfa0261b76082774af8567c20d7c5c0134ed20619db380b063af3edbc75b3313532e210ad9943dbf419b9edd"), 94 | }, 95 | true, 96 | }, 97 | { 98 | "carlos-success", 99 | carlosSECP256R1PublicKey, 100 | args{ 101 | message: []byte("hello from mailchain"), 102 | sig: encodingtest.MustDecodeHex("d6be4b13413fe3c1ca7562140a52ad14465f81d7a1ebc5eb74e3e5ef22126dfc25046c25ad5a09a67754161aed356a99a4b034e71aec36d714199c9e37485f56"), 103 | }, 104 | true, 105 | }, 106 | { 107 | "carlos-success-ts-compatibility", 108 | carlosSECP256R1PublicKey, 109 | args{ 110 | message: []byte("hello from mailchain"), 111 | sig: encodingtest.MustDecodeHex("8a7b2047bbb834a21f32ff5d1082c6810cdefbb4c7c28a037e59711866b2134306066ef68e9ec2f42b544736003771c21e318649274e1c10b766e032a9f3bdc2"), 112 | }, 113 | true, 114 | }, 115 | } 116 | for _, tt := range tests { 117 | t.Run(tt.name, func(t *testing.T) { 118 | pk := tt.target 119 | digest := blake2b.Sum256(tt.args.message) 120 | assert.Equal(t, tt.want, pk.Verify(digest[:], tt.args.sig)) 121 | }) 122 | } 123 | } 124 | 125 | func TestPublicKeyFromBytes(t *testing.T) { 126 | type args struct { 127 | keyBytes []byte 128 | } 129 | tests := []struct { 130 | name string 131 | args args 132 | want crypto.PublicKey 133 | assertion assert.ErrorAssertionFunc 134 | }{ 135 | { 136 | "alice", 137 | args{ 138 | aliceSECP256R1PublicKeyBytes, 139 | }, 140 | &aliceSECP256R1PublicKey, 141 | assert.NoError, 142 | }, 143 | { 144 | "bob", 145 | args{ 146 | bobSECP256R1PublicKeyBytes, 147 | }, 148 | &bobSECP256R1PublicKey, 149 | assert.NoError, 150 | }, 151 | { 152 | "carlos", 153 | args{ 154 | carlosSECP256R1PublicKeyBytes, 155 | }, 156 | &carlosSECP256R1PublicKey, 157 | assert.NoError, 158 | }, 159 | } 160 | for _, tt := range tests { 161 | t.Run(tt.name, func(t *testing.T) { 162 | got, err := PublicKeyFromBytes(tt.args.keyBytes) 163 | tt.assertion(t, err) 164 | assert.Equal(t, tt.want, got) 165 | }) 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /cipher/aes256cbc/decrypter_test.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | 7 | "github.com/mailchain/go-crypto/cipher" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-encoding/encodingtest" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestDecrypter(t *testing.T) { 14 | cases := []struct { 15 | name string 16 | decrypter Decrypter 17 | encryptedData cipher.EncryptedContent 18 | expected []byte 19 | err error 20 | }{ 21 | {"to-alice-short-text", 22 | Decrypter{privateKey: secp256k1test.AlicePrivateKey}, 23 | encodingtest.MustDecodeHex("2efafed52c39d4cd1ef32db24d015e77e002882fde5ee55e0b49d2b84ccd1bbe19ee705b355421e9f7e88edbce5b4b1da6ba08ce4c5adca03ea8f14b45ac09a0c2536b70c0a72cc01a5310b240508ff2cbded2b74094e6d302707b324e43ace545e2"), 24 | []byte("Hi Sofia"), 25 | nil, 26 | }, {"to-alice-medium-text", 27 | Decrypter{privateKey: secp256k1test.AlicePrivateKey}, 28 | encodingtest.MustDecodeHex("2ee80337544404fb07b06cd19515fcd635038621ccd7fb04c7e2771ea1b6ccd3dcacba071bdd92a51ee443c3735a09c32c4d5523950142380c9ac771d95eece221470a3a52db70060ff43dbea5d3891da942c14f515f9f0bc9fa1f7cc25327b8b67668f75a266de53ec000e04375fab30c67adced120090c5dcb7b3bfde491239bb3c6a444aff4610c905f1e8ec82ea0d9f54a6d31e195b4f784e150762be160f52732b99eb503ba42da8eb2baef63adc931"), 29 | []byte("Hi Sofia, this is a little bit of a longer message to make sure there are no problems"), 30 | nil, 31 | }, {"to-alice-short-text", 32 | Decrypter{privateKey: secp256k1test.BobPrivateKey}, 33 | encodingtest.MustDecodeHex("2e5e33a1a6013a268a7494ffc27a27a5a903c1715fed0ebf62975efea659152054adb11d5e4c43f4894cd5a233da10f33175e4afa699faf96dccfe551e4a11a4d8334f8bce4ec04e3119852b283b55ce208148ce7190b8affe0c6b5b6430bb749576"), 34 | []byte("Hi Charlotte"), 35 | nil, 36 | }, {"to-alice-short-text", 37 | Decrypter{privateKey: secp256k1test.BobPrivateKey}, 38 | encodingtest.MustDecodeHex("2e7ffbd8a1092e8cc8f6a0a5df4a23ea0b02fba947d56d83a9d6801233ce074ed936811d9d541be757b8b39bdc9d0ff08424b2bd1c5dc26e5789ef4fa5bfb24e074acd8c0a324d3a0598bde06f4d51ac1bc91eed372e93bb23891840882eac1008f56ed77171de248dd7ed0dd33f8ed6b4d9fde39ed690aea3d33f9a4dd09f645fea625603e5ad06c44ced1c2c44d1a2c895b91f5dd8d1a1d540f8f9e88087c3466c359c4e6a8347ee54dbfdd055ede2d29e"), 39 | []byte("Hi Charlotte, this is a little bit of a longer message to make sure there are no problems"), 40 | nil, 41 | }, 42 | } 43 | for _, tc := range cases { 44 | t.Run(tc.name, func(t *testing.T) { 45 | res, err := tc.decrypter.Decrypt(tc.encryptedData) 46 | assert.EqualValues(t, string(tc.expected), string(res)) 47 | assert.Equal(t, tc.err, err) 48 | }) 49 | } 50 | } 51 | 52 | func Test_decryptCBC(t *testing.T) { 53 | type args struct { 54 | key []byte 55 | iv []byte 56 | ciphertext []byte 57 | } 58 | tests := []struct { 59 | name string 60 | args args 61 | want []byte 62 | wantErr bool 63 | }{ 64 | { 65 | "success-short-text", 66 | args{ 67 | encodingtest.MustDecodeHex("af0ad81e7d9194721d6c26f6c1f2a2b7fd06e2c99c4f5deefe59fb93936c981e"), 68 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 69 | encodingtest.MustDecodeHex("747ef78a32eb582d325a634e4acffd61"), 70 | }, 71 | []byte("Hi Tim"), 72 | false, 73 | }, 74 | { 75 | "success-medium-text", 76 | args{ 77 | encodingtest.MustDecodeHex("af0ad81e7d9194721d6c26f6c1f2a2b7fd06e2c99c4f5deefe59fb93936c981e"), 78 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 79 | encodingtest.MustDecodeHex("2ec66aac453ff543f47830d4b8cbc68d9965bf7c6bb69724fd4de26d41001256dfa6f7f0b3956ce21d4717caf75b0c2ad753852f216df6cfbcda4911619c5fc34798a19f81adff902c1ad906ab0edaec"), 80 | }, 81 | []byte("Hi Tim, this is a much longer message to make sure there are no problems"), 82 | false, 83 | }, 84 | { 85 | "err-key", 86 | args{ 87 | nil, 88 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 89 | encodingtest.MustDecodeHex("2ec66aac453ff543f47830d4b8cbc68d9965bf7c6bb69724fd4de26d41001256dfa6f7f0b3956ce21d4717caf75b0c2ad753852f216df6cfbcda4911619c5fc34798a19f81adff902c1ad906ab0edaec"), 90 | }, 91 | nil, 92 | true, 93 | }} 94 | for _, tt := range tests { 95 | t.Run(tt.name, func(t *testing.T) { 96 | got, err := decryptCBC(tt.args.key, tt.args.iv, tt.args.ciphertext) 97 | if (err != nil) != tt.wantErr { 98 | t.Errorf("decryptCBC() error = %v, wantErr %v", err, tt.wantErr) 99 | return 100 | } 101 | if !reflect.DeepEqual(got, tt.want) { 102 | t.Errorf("decryptCBC() = %v, want %v", got, tt.want) 103 | } 104 | }) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /multikey/public_test.go: -------------------------------------------------------------------------------- 1 | package multikey 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 8 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 9 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestPublicKeyFromBytes(t *testing.T) { 14 | type args struct { 15 | hex string 16 | keyType []byte 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | wantBytes []byte 22 | wantErr bool 23 | }{ 24 | { 25 | "secp256k1", 26 | args{ 27 | "secp256k1", 28 | secp256k1test.AlicePublicKey.Bytes(), 29 | }, 30 | secp256k1test.AlicePublicKey.Bytes(), 31 | false, 32 | }, 33 | { 34 | "ed25519", 35 | args{ 36 | "ed25519", 37 | ed25519test.AlicePublicKey.Bytes(), 38 | }, 39 | ed25519test.AlicePublicKey.Bytes(), 40 | false, 41 | }, 42 | { 43 | "sr25519", 44 | args{ 45 | "sr25519", 46 | sr25519test.AlicePublicKey.Bytes(), 47 | }, 48 | sr25519test.AlicePublicKey.Bytes(), 49 | false, 50 | }, 51 | { 52 | "err", 53 | args{ 54 | "unknown", 55 | secp256k1test.AlicePublicKey.Bytes(), 56 | }, 57 | nil, 58 | true, 59 | }, 60 | } 61 | for _, tt := range tests { 62 | t.Run(tt.name, func(t *testing.T) { 63 | got, err := PublicKeyFromBytes(tt.args.hex, tt.args.keyType) 64 | if (err != nil) != tt.wantErr { 65 | t.Errorf("PublicKeyFromBytes() error = %v, wantErr %v", err, tt.wantErr) 66 | } 67 | if got != nil { 68 | if !assert.EqualValues(t, tt.wantBytes, got.Bytes()) { 69 | t.Errorf("PublicKeyFromBytes() = %v, want %v", got, tt.wantBytes) 70 | } 71 | } 72 | if got == nil { 73 | if !assert.Nil(t, tt.wantBytes) { 74 | t.Errorf("PublicKeyFromBytes() = %v, want %v", got, tt.wantBytes) 75 | } 76 | } 77 | 78 | }) 79 | } 80 | } 81 | 82 | func TestDescriptivePublicKeyFromBytes(t *testing.T) { 83 | type args struct { 84 | in []byte 85 | } 86 | tests := []struct { 87 | name string 88 | args args 89 | wantBytes []byte 90 | wantErr bool 91 | }{ 92 | { 93 | "secp256k1", 94 | args{ 95 | append([]byte{crypto.IDSECP256K1}, secp256k1test.AlicePublicKey.Bytes()...), 96 | }, 97 | secp256k1test.AlicePublicKey.Bytes(), 98 | false, 99 | }, 100 | { 101 | "ed25519", 102 | args{ 103 | append([]byte{crypto.IDED25519}, ed25519test.AlicePublicKey.Bytes()...), 104 | }, 105 | ed25519test.AlicePublicKey.Bytes(), 106 | false, 107 | }, 108 | { 109 | "sr25519", 110 | args{ 111 | append([]byte{crypto.IDSR25519}, sr25519test.AlicePublicKey.Bytes()...), 112 | }, 113 | sr25519test.AlicePublicKey.Bytes(), 114 | false, 115 | }, 116 | { 117 | "err", 118 | args{ 119 | append([]byte{0x00}, sr25519test.AlicePublicKey.Bytes()...), 120 | }, 121 | nil, 122 | true, 123 | }, 124 | } 125 | for _, tt := range tests { 126 | t.Run(tt.name, func(t *testing.T) { 127 | got, err := DescriptivePublicKeyFromBytes(tt.args.in) 128 | if (err != nil) != tt.wantErr { 129 | t.Errorf("DescriptivePublicKeyFromBytes() error = %v, wantErr %v", err, tt.wantErr) 130 | return 131 | } 132 | if got != nil { 133 | if !assert.EqualValues(t, tt.wantBytes, got.Bytes()) { 134 | t.Errorf("DescriptivePublicKeyFromBytes() = %v, want %v", got, tt.wantBytes) 135 | } 136 | } 137 | if got == nil { 138 | if !assert.Nil(t, tt.wantBytes) { 139 | t.Errorf("DescriptivePublicKeyFromBytes() = %v, want %v", got, tt.wantBytes) 140 | } 141 | } 142 | }) 143 | } 144 | } 145 | 146 | func TestDescriptiveBytesFromPublicKey(t *testing.T) { 147 | type args struct { 148 | in crypto.PublicKey 149 | } 150 | tests := []struct { 151 | name string 152 | args args 153 | want []byte 154 | assertion assert.ErrorAssertionFunc 155 | }{ 156 | { 157 | "secp256k1", 158 | args{ 159 | secp256k1test.AlicePublicKey, 160 | }, 161 | append([]byte{crypto.IDSECP256K1}, secp256k1test.AlicePublicKey.Bytes()...), 162 | assert.NoError, 163 | }, 164 | { 165 | "ed25519", 166 | args{ 167 | ed25519test.AlicePublicKey, 168 | }, 169 | append([]byte{crypto.IDED25519}, ed25519test.AlicePublicKey.Bytes()...), 170 | assert.NoError, 171 | }, 172 | { 173 | "sr25519", 174 | args{ 175 | sr25519test.AlicePublicKey, 176 | }, 177 | append([]byte{crypto.IDSR25519}, sr25519test.AlicePublicKey.Bytes()...), 178 | assert.NoError, 179 | }, 180 | { 181 | "err", 182 | args{ 183 | nil, 184 | }, 185 | nil, 186 | assert.Error, 187 | }, 188 | } 189 | for _, tt := range tests { 190 | t.Run(tt.name, func(t *testing.T) { 191 | got, err := DescriptiveBytesFromPublicKey(tt.args.in) 192 | tt.assertion(t, err) 193 | assert.Equal(t, tt.want, got) 194 | }) 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /cipher/ecdh/ecdh_test.go: -------------------------------------------------------------------------------- 1 | package ecdh 2 | 3 | import ( 4 | "crypto/rand" 5 | "testing" 6 | 7 | "github.com/mailchain/go-crypto" 8 | "github.com/mailchain/go-crypto/cipher" 9 | "github.com/mailchain/go-crypto/ed25519" 10 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 11 | "github.com/mailchain/go-crypto/secp256k1" 12 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 13 | "github.com/mailchain/go-crypto/sr25519" 14 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func Test_SharedSecretEndToEnd(t *testing.T) { 19 | type args struct { 20 | keyExchange cipher.KeyExchange 21 | RecipientPrivateKey crypto.PrivateKey 22 | } 23 | tests := []struct { 24 | name string 25 | args args 26 | }{ 27 | { 28 | "secp256k1-random", 29 | args{ 30 | func() cipher.KeyExchange { 31 | kx, _ := NewSECP256K1(rand.Reader) 32 | return kx 33 | }(), 34 | func() crypto.PrivateKey { 35 | pk, err := secp256k1.GenerateKey(rand.Reader) 36 | if err != nil { 37 | assert.FailNow(t, "secp256k1.GenerateKey error = %v", err) 38 | } 39 | return pk 40 | }(), 41 | }, 42 | }, 43 | { 44 | "secp256k1-bob", 45 | args{ 46 | func() cipher.KeyExchange { 47 | kx, _ := NewSECP256K1(rand.Reader) 48 | return kx 49 | }(), 50 | secp256k1test.BobPrivateKey, 51 | }, 52 | }, 53 | { 54 | "secp256k1-alice", 55 | args{ 56 | func() cipher.KeyExchange { 57 | kx, _ := NewSECP256K1(rand.Reader) 58 | return kx 59 | }(), 60 | secp256k1test.AlicePrivateKey, 61 | }, 62 | }, 63 | { 64 | "ed25519-random", 65 | args{ 66 | func() cipher.KeyExchange { 67 | kx, _ := NewED25519(rand.Reader) 68 | return kx 69 | }(), 70 | func() crypto.PrivateKey { 71 | pk, err := ed25519.GenerateKey(rand.Reader) 72 | if err != nil { 73 | assert.FailNow(t, "ed25519.GenerateKey error = %v", err) 74 | } 75 | return pk 76 | }(), 77 | }, 78 | }, 79 | { 80 | "ed25519-alice", 81 | args{ 82 | func() cipher.KeyExchange { 83 | kx, _ := NewED25519(rand.Reader) 84 | return kx 85 | }(), 86 | ed25519test.AlicePrivateKey, 87 | }, 88 | }, 89 | { 90 | "ed25519-bob", 91 | args{ 92 | func() cipher.KeyExchange { 93 | kx, _ := NewED25519(rand.Reader) 94 | return kx 95 | }(), 96 | ed25519test.BobPrivateKey, 97 | }, 98 | }, 99 | { 100 | "sr25519-random", 101 | args{ 102 | func() cipher.KeyExchange { 103 | kx, _ := NewSR25519(rand.Reader) 104 | return kx 105 | }(), 106 | func() crypto.PrivateKey { 107 | pk, err := sr25519.GenerateKey(rand.Reader) 108 | if err != nil { 109 | assert.FailNow(t, "sr25519.GenerateKey error = %v", err) 110 | } 111 | return pk 112 | }(), 113 | }, 114 | }, 115 | { 116 | "sr25519-alice", 117 | args{ 118 | func() cipher.KeyExchange { 119 | kx, _ := NewSR25519(rand.Reader) 120 | return kx 121 | }(), 122 | sr25519test.AlicePrivateKey, 123 | }, 124 | }, 125 | { 126 | "sr25519-bob", 127 | args{ 128 | func() cipher.KeyExchange { 129 | kx, _ := NewSR25519(rand.Reader) 130 | return kx 131 | }(), 132 | sr25519test.BobPrivateKey, 133 | }, 134 | }, 135 | } 136 | for _, tt := range tests { 137 | t.Run(tt.name, func(t *testing.T) { 138 | ephemeralPrivKey, err := tt.args.keyExchange.EphemeralKey() 139 | if err != nil { 140 | assert.Fail(t, "EphemeralKey() error = %v", err) 141 | return 142 | } 143 | 144 | senderSharedSecret, err := tt.args.keyExchange.SharedSecret(ephemeralPrivKey, tt.args.RecipientPrivateKey.PublicKey()) 145 | if err != nil { 146 | assert.Fail(t, "SharedSecret() error = %v", err) 147 | return 148 | } 149 | recipientSharedSecret, err := tt.args.keyExchange.SharedSecret(tt.args.RecipientPrivateKey, ephemeralPrivKey.PublicKey()) 150 | if err != nil { 151 | assert.Fail(t, "SharedSecret() error = %v", err) 152 | return 153 | } 154 | controlPrivKey, err := tt.args.keyExchange.EphemeralKey() 155 | if err != nil { 156 | assert.Fail(t, "EphemeralKey() error = %v", err) 157 | return 158 | } 159 | controlSenderSharedSecret, err := tt.args.keyExchange.SharedSecret(ephemeralPrivKey, controlPrivKey.PublicKey()) 160 | if err != nil { 161 | assert.Fail(t, "SharedSecret() error = %v", err) 162 | return 163 | } 164 | controlRecipientSharedSecret, err := tt.args.keyExchange.SharedSecret(controlPrivKey, ephemeralPrivKey.PublicKey()) 165 | if err != nil { 166 | assert.Fail(t, "SharedSecret() error = %v", err) 167 | return 168 | } 169 | assert.Equal(t, senderSharedSecret, recipientSharedSecret) 170 | assert.NotEqual(t, controlSenderSharedSecret, recipientSharedSecret) 171 | assert.NotEqual(t, controlRecipientSharedSecret, recipientSharedSecret) 172 | }) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /ed25519/derive_test.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/mailchain/go-crypto" 7 | "github.com/mailchain/go-crypto/chaincode" 8 | "github.com/mailchain/go-encoding/encodingtest" 9 | "github.com/stretchr/testify/assert" 10 | "github.com/stretchr/testify/require" 11 | ) 12 | 13 | func TestDeriveHardenedKey(t *testing.T) { 14 | type args struct { 15 | parent crypto.PrivateKey 16 | path []uint64 17 | } 18 | tests := []struct { 19 | name string 20 | args args 21 | want *PrivateKey 22 | }{ 23 | { 24 | "alice://0", 25 | args{ 26 | &alicePrivateKey, 27 | []uint64{0}, 28 | }, 29 | func() *PrivateKey { 30 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0x860feceae0ebccf975cc85092a38ae24e4674e55d3d6aa707a18de71358ccc33")) 31 | require.NoError(t, err) 32 | return p 33 | }(), 34 | }, 35 | { 36 | "alice://1", 37 | args{ 38 | &alicePrivateKey, 39 | []uint64{1}, 40 | }, 41 | func() *PrivateKey { 42 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0x729b9cbc57779d707d587e5f860a7cd3db8804ae39f755cd0036fda853da2139")) 43 | require.NoError(t, err) 44 | return p 45 | }(), 46 | }, 47 | { 48 | "alice://1//2", 49 | args{ 50 | &alicePrivateKey, 51 | []uint64{1, 2}, 52 | }, 53 | func() *PrivateKey { 54 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xed9ab0b26b9a3e6d48d55030ba15ec66823fe7d12ca8fad690a8d4bc9b9488cc")) 55 | require.NoError(t, err) 56 | return p 57 | }(), 58 | }, 59 | { 60 | "alice://1://2", 61 | args{ 62 | func() *PrivateKey { 63 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0x729b9cbc57779d707d587e5f860a7cd3db8804ae39f755cd0036fda853da2139")) 64 | require.NoError(t, err) 65 | return p 66 | }(), 67 | []uint64{2}, 68 | }, 69 | func() *PrivateKey { 70 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xed9ab0b26b9a3e6d48d55030ba15ec66823fe7d12ca8fad690a8d4bc9b9488cc")) 71 | require.NoError(t, err) 72 | return p 73 | }(), 74 | }, 75 | 76 | { 77 | "bob://0", 78 | args{ 79 | &bobPrivateKey, 80 | []uint64{0}, 81 | }, 82 | func() *PrivateKey { 83 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xccd684257e55f16dd50eea4e52bd04843716e13295a542a143a09792c419191c")) 84 | require.NoError(t, err) 85 | return p 86 | }(), 87 | }, 88 | { 89 | "bob://1", 90 | args{ 91 | &bobPrivateKey, 92 | []uint64{1}, 93 | }, 94 | func() *PrivateKey { 95 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0x76f7e8aa3e95bfc13d4ab8b59f6bd82ad3621449fbb66c123cc9c310c7d8d286")) 96 | require.NoError(t, err) 97 | return p 98 | }(), 99 | }, 100 | { 101 | "bob://1//2", 102 | args{ 103 | &bobPrivateKey, 104 | []uint64{1, 2}, 105 | }, 106 | func() *PrivateKey { 107 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xd2c0014e75ccce7b3319f5be5f494e9af56b9596fb7546af0eaef4a9c7caecc3")) 108 | require.NoError(t, err) 109 | return p 110 | }(), 111 | }, 112 | { 113 | "bob://1://2", 114 | args{ 115 | func() *PrivateKey { 116 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0x76f7e8aa3e95bfc13d4ab8b59f6bd82ad3621449fbb66c123cc9c310c7d8d286")) 117 | require.NoError(t, err) 118 | return p 119 | }(), 120 | []uint64{2}, 121 | }, 122 | func() *PrivateKey { 123 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xd2c0014e75ccce7b3319f5be5f494e9af56b9596fb7546af0eaef4a9c7caecc3")) 124 | require.NoError(t, err) 125 | return p 126 | }(), 127 | }, 128 | } 129 | for _, tt := range tests { 130 | t.Run(tt.name, func(t *testing.T) { 131 | var parent = tt.args.parent 132 | var err error 133 | for _, item := range tt.args.path { 134 | 135 | parent, err = DeriveHardenedKey(parent, chaincode.ChainCodeFromDeriveIndexUint64(item)) 136 | require.NoError(t, err) 137 | } 138 | assert.Equal(t, tt.want, parent) 139 | }) 140 | } 141 | } 142 | 143 | func TestDeriveHardenedKeyString(t *testing.T) { 144 | type args struct { 145 | parent crypto.PrivateKey 146 | path []string 147 | } 148 | tests := []struct { 149 | name string 150 | args args 151 | want *PrivateKey 152 | }{ 153 | { 154 | "bob://1", 155 | args{ 156 | &bobPrivateKey, 157 | []string{"test.string"}, 158 | }, 159 | func() *PrivateKey { 160 | p, err := PrivateKeyFromBytes(encodingtest.MustDecodeHexZeroX("0x8a4f41889ae03047e2427ec156be5505fa64374007a70aa4ee191c7a76f8e3a4")) 161 | require.NoError(t, err) 162 | return p 163 | }(), 164 | }, 165 | } 166 | for _, tt := range tests { 167 | t.Run(tt.name, func(t *testing.T) { 168 | var parent = tt.args.parent 169 | var err error 170 | for _, item := range tt.args.path { 171 | 172 | parent, err = DeriveHardenedKey(parent, chaincode.ChainCodeFromDeriveIndexString(item)) 173 | require.NoError(t, err) 174 | } 175 | assert.Equal(t, tt.want, parent) 176 | }) 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /ed25519/private_test.go: -------------------------------------------------------------------------------- 1 | package ed25519 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "io" 7 | "testing" 8 | "testing/iotest" 9 | 10 | "github.com/mailchain/go-crypto" 11 | "github.com/mailchain/go-encoding/encodingtest" 12 | "github.com/stretchr/testify/assert" 13 | ) 14 | 15 | func TestPrivateKey_Bytes(t *testing.T) { 16 | tests := []struct { 17 | name string 18 | pk PrivateKey 19 | want []byte 20 | }{ 21 | { 22 | "alice", 23 | alicePrivateKey, 24 | aliceKeyPair, 25 | }, 26 | { 27 | "bob", 28 | bobPrivateKey, 29 | bobKeyPair, 30 | }, 31 | } 32 | for _, tt := range tests { 33 | t.Run(tt.name, func(t *testing.T) { 34 | if got := tt.pk.Bytes(); !assert.Equal(t, tt.want, got) { 35 | t.Errorf("PrivateKey.Bytes() = %v, want %v", got, tt.want) 36 | } 37 | }) 38 | } 39 | } 40 | 41 | func TestPrivateKeyFromBytes(t *testing.T) { 42 | type args struct { 43 | pk []byte 44 | } 45 | tests := []struct { 46 | name string 47 | args args 48 | want *PrivateKey 49 | wantErr bool 50 | }{ 51 | { 52 | "success-alice-seed", 53 | args{ 54 | aliceSeed, 55 | }, 56 | &alicePrivateKey, 57 | false, 58 | }, 59 | { 60 | "success-alice-bytes", 61 | args{ 62 | alicePrivateKeyBytes, 63 | }, 64 | &alicePrivateKey, 65 | false, 66 | }, 67 | { 68 | "success-bob-seed", 69 | args{ 70 | bobSeed, 71 | }, 72 | &bobPrivateKey, 73 | false, 74 | }, 75 | { 76 | "success-bob-bytes", 77 | args{ 78 | bobPrivateKeyBytes, 79 | }, 80 | &bobPrivateKey, 81 | false, 82 | }, 83 | { 84 | "err-len", 85 | args{ 86 | encodingtest.MustDecodeHex("39d4c9"), 87 | }, 88 | nil, 89 | true, 90 | }, 91 | } 92 | for _, tt := range tests { 93 | t.Run(tt.name, func(t *testing.T) { 94 | got, err := PrivateKeyFromBytes(tt.args.pk) 95 | if (err != nil) != tt.wantErr { 96 | t.Errorf("PrivateKeyFromBytes() error = %v, wantErr %v", err, tt.wantErr) 97 | return 98 | } 99 | if !assert.Equal(t, tt.want, got) { 100 | t.Errorf("PrivateKeyFromBytes() = %v, want %v", got, tt.want) 101 | } 102 | }) 103 | } 104 | } 105 | 106 | func TestPrivateKey_PublicKey(t *testing.T) { 107 | tests := []struct { 108 | name string 109 | pk PrivateKey 110 | want crypto.PublicKey 111 | }{ 112 | { 113 | "alice", 114 | alicePrivateKey, 115 | &alicePublicKey, 116 | }, 117 | { 118 | "bob", 119 | bobPrivateKey, 120 | &bobPublicKey, 121 | }, 122 | } 123 | for _, tt := range tests { 124 | t.Run(tt.name, func(t *testing.T) { 125 | if got := tt.pk.PublicKey(); !assert.Equal(t, tt.want, got) { 126 | t.Errorf("PrivateKey.PublicKey() = %v, want %v", got, tt.want) 127 | } 128 | }) 129 | } 130 | } 131 | 132 | func TestPrivateKey_Sign(t *testing.T) { 133 | tests := []struct { 134 | name string 135 | pk PrivateKey 136 | msg []byte 137 | want []byte 138 | wantErr bool 139 | }{ 140 | { 141 | "success-bob", 142 | bobPrivateKey, 143 | []byte("message"), 144 | []byte{0x7d, 0x51, 0xea, 0xfa, 0x52, 0x78, 0x31, 0x69, 0xd0, 0xa9, 0x4a, 0xc, 0x9f, 0x2b, 0xca, 0xd5, 0xe0, 0x3d, 0x29, 0x17, 0x33, 0x0, 0x93, 0xf, 0xf3, 0xc7, 0xd6, 0x3b, 0xfd, 0x64, 0x17, 0xae, 0x1b, 0xc8, 0x1f, 0xef, 0x51, 0xba, 0x14, 0x9a, 0xe8, 0xa1, 0xe1, 0xda, 0xe0, 0x5f, 0xdc, 0xa5, 0x7, 0x8b, 0x14, 0xba, 0xc4, 0xcf, 0x26, 0xcc, 0xc6, 0x1, 0x1e, 0x5e, 0xab, 0x77, 0x3, 0xc}, 145 | false, 146 | }, 147 | { 148 | "success-alice", 149 | alicePrivateKey, 150 | []byte("egassem"), 151 | []byte{0xde, 0x6c, 0x88, 0xe6, 0x9c, 0x9f, 0x93, 0xb, 0x59, 0xdd, 0xf4, 0x80, 0xc2, 0x9a, 0x55, 0x79, 0xec, 0x89, 0x5c, 0xa9, 0x7a, 0x36, 0xf6, 0x69, 0x74, 0xc1, 0xf0, 0x15, 0x5c, 0xc0, 0x66, 0x75, 0x2e, 0xcd, 0x9a, 0x9b, 0x41, 0x35, 0xd2, 0x72, 0x32, 0xe0, 0x54, 0x80, 0xbc, 0x98, 0x58, 0x1, 0xa9, 0xfd, 0xe4, 0x27, 0xc7, 0xef, 0xa5, 0x42, 0x5f, 0xf, 0x46, 0x49, 0xb8, 0xad, 0xbd, 0x5}, 152 | false, 153 | }, 154 | { 155 | "err-len", 156 | PrivateKey{ 157 | Key: []byte{0xd, 0x9b, 0x4a, 0x3c, 0x10, 0x72, 0x19, 0x91, 0xc6, 0xb8, 0x6, 0xf0, 0xf3, 0x43, 0x53, 0x5d, 0xc2, 0xb4, 0x6c, 0x74}, 158 | }, 159 | []byte("message"), 160 | nil, 161 | true, 162 | }, 163 | } 164 | for _, tt := range tests { 165 | t.Run(tt.name, func(t *testing.T) { 166 | got, err := tt.pk.Sign(tt.msg) 167 | if (err != nil) != tt.wantErr { 168 | t.Errorf("Sign() error = %v, wantErr %v", err, tt.wantErr) 169 | return 170 | } 171 | if !assert.Equal(t, tt.want, got) { 172 | t.Errorf("Sign() = %v,\n want %v", got, tt.want) 173 | } 174 | }) 175 | } 176 | } 177 | 178 | func TestGenerateKey(t *testing.T) { 179 | type args struct { 180 | rand io.Reader 181 | } 182 | tests := []struct { 183 | name string 184 | args args 185 | wantNil bool 186 | wantErr bool 187 | }{ 188 | { 189 | "success", 190 | args{ 191 | rand.Reader, 192 | }, 193 | false, 194 | false, 195 | }, 196 | { 197 | "err-rand", 198 | args{ 199 | iotest.DataErrReader(bytes.NewReader(nil)), 200 | }, 201 | true, 202 | true, 203 | }, 204 | } 205 | for _, tt := range tests { 206 | t.Run(tt.name, func(t *testing.T) { 207 | got, err := GenerateKey(tt.args.rand) 208 | if (err != nil) != tt.wantErr { 209 | t.Errorf("GenerateKey() error = %v, wantErr %v", err, tt.wantErr) 210 | return 211 | } 212 | if (got == nil) != tt.wantNil { 213 | t.Errorf("GenerateKey() = %v, want %v", got, tt.wantNil) 214 | } 215 | }) 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /cipher/aes256cbc/encrypter_test.go: -------------------------------------------------------------------------------- 1 | package aes256cbc 2 | 3 | import ( 4 | "testing" 5 | 6 | ethcypto "github.com/ethereum/go-ethereum/crypto" 7 | "github.com/ethereum/go-ethereum/crypto/ecies" 8 | "github.com/mailchain/go-crypto/secp256k1" 9 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 10 | "github.com/mailchain/go-encoding/encodingtest" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func Test_encryptCBC(t *testing.T) { 15 | type args struct { 16 | data []byte 17 | iv []byte 18 | key []byte 19 | } 20 | tests := []struct { 21 | name string 22 | args args 23 | want []byte 24 | wantErr bool 25 | }{ 26 | { 27 | "success-short-text", 28 | args{ 29 | []byte("Hi Tim"), 30 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 31 | encodingtest.MustDecodeHex("af0ad81e7d9194721d6c26f6c1f2a2b7fd06e2c99c4f5deefe59fb93936c981e"), 32 | }, 33 | encodingtest.MustDecodeHex("747ef78a32eb582d325a634e4acffd61"), 34 | false, 35 | }, 36 | { 37 | "success-medium-text", 38 | args{ 39 | []byte("Hi Tim, this is a much longer message to make sure there are no problems"), 40 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 41 | encodingtest.MustDecodeHex("af0ad81e7d9194721d6c26f6c1f2a2b7fd06e2c99c4f5deefe59fb93936c981e"), 42 | }, 43 | encodingtest.MustDecodeHex("2ec66aac453ff543f47830d4b8cbc68d9965bf7c6bb69724fd4de26d41001256dfa6f7f0b3956ce21d4717caf75b0c2ad753852f216df6cfbcda4911619c5fc34798a19f81adff902c1ad906ab0edaec"), 44 | false, 45 | }, 46 | { 47 | "err-iv", 48 | args{ 49 | []byte("Hi Tim, this is a much longer message to make sure there are no problems"), 50 | encodingtest.MustDecodeHex("0505"), 51 | encodingtest.MustDecodeHex("af0ad81e7d9194721d6c26f6c1f2a2b7fd06e2c99c4f5deefe59fb93936c981e"), 52 | }, 53 | nil, 54 | true, 55 | }, 56 | { 57 | "err-key", 58 | args{ 59 | []byte("Hi Tim, this is a much longer message to make sure there are no problems"), 60 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 61 | encodingtest.MustDecodeHex("af"), 62 | }, 63 | nil, 64 | true, 65 | }, 66 | } 67 | for _, tt := range tests { 68 | t.Run(tt.name, func(t *testing.T) { 69 | got, err := encryptCBC(tt.args.data, tt.args.iv, tt.args.key) 70 | if (err != nil) != tt.wantErr { 71 | t.Errorf("encryptCBC() error = %v, wantErr %v", err, tt.wantErr) 72 | return 73 | } 74 | if !assert.Equal(t, tt.want, got) { 75 | t.Errorf("encryptCBC() = %v, want %v", got, tt.want) 76 | } 77 | }) 78 | } 79 | } 80 | 81 | func Test_encrypt(t *testing.T) { 82 | type args struct { 83 | ephemeralPrivateKey *ecies.PrivateKey 84 | pub *ecies.PublicKey 85 | input []byte 86 | iv []byte 87 | } 88 | tests := []struct { 89 | name string 90 | args args 91 | want *encryptedData 92 | wantErr bool 93 | }{ 94 | { 95 | "success", 96 | args{ 97 | func() *ecies.PrivateKey { 98 | tmpEphemeralPrivateKey, err := ethcypto.HexToECDSA("0404040404040404040404040404040404040404040404040404040404040404") 99 | if err != nil { 100 | t.Fatal(err) 101 | } 102 | return ecies.ImportECDSA(tmpEphemeralPrivateKey) 103 | }(), 104 | func() *ecies.PublicKey { 105 | tp, ok := secp256k1test.AlicePublicKey.(*secp256k1.PublicKey) 106 | if !ok { 107 | t.Error("failed to cast") 108 | } 109 | pub, err := tp.ECIES() 110 | if err != nil { 111 | t.Error(err) 112 | } 113 | return pub 114 | }(), 115 | []byte("Hi Tim, this is a much longer message to make sure there are no problems"), 116 | encodingtest.MustDecodeHex("05050505050505050505050505050505"), 117 | }, 118 | &encryptedData{ 119 | Ciphertext: encodingtest.MustDecodeHex("2ec66aac453ff543f47830d4b8cbc68d9965bf7c6bb69724fd4de26d41001256dfa6f7f0b3956ce21d4717caf75b0c2ad753852f216df6cfbcda4911619c5fc34798a19f81adff902c1ad906ab0edaec"), 120 | EphemeralPublicKey: encodingtest.MustDecodeHex("04462779ad4aad39514614751a71085f2f10e1c7a593e4e030efb5b8721ce55b0b199c07969f5442000bea455d72ae826a86bfac9089cb18152ed756ebb2a596f5"), 121 | InitializationVector: encodingtest.MustDecodeHex("05050505050505050505050505050505"), 122 | MessageAuthenticationCode: encodingtest.MustDecodeHex("4367ae8a54b65f99e4f2fd315ba65bf85e1138967a7bea451faf80f75cdf3404"), 123 | }, 124 | false, 125 | }, 126 | { 127 | "err-encryptCBC-invalid-iv", 128 | args{ 129 | func() *ecies.PrivateKey { 130 | tmpEphemeralPrivateKey, err := ethcypto.HexToECDSA("0404040404040404040404040404040404040404040404040404040404040404") 131 | if err != nil { 132 | t.Fatal(err) 133 | } 134 | return ecies.ImportECDSA(tmpEphemeralPrivateKey) 135 | }(), 136 | func() *ecies.PublicKey { 137 | tp, ok := secp256k1test.AlicePublicKey.(*secp256k1.PublicKey) 138 | if !ok { 139 | t.Error("failed to cast") 140 | } 141 | pub, err := tp.ECIES() 142 | if err != nil { 143 | t.Error(err) 144 | } 145 | return pub 146 | }(), 147 | []byte("Hi Tim, this is a much longer message to make sure there are no problems"), 148 | encodingtest.MustDecodeHex("0505"), 149 | }, 150 | nil, 151 | true, 152 | }, 153 | } 154 | for _, tt := range tests { 155 | t.Run(tt.name, func(t *testing.T) { 156 | got, err := encrypt(tt.args.ephemeralPrivateKey, tt.args.pub, tt.args.input, tt.args.iv) 157 | if (err != nil) != tt.wantErr { 158 | t.Errorf("encrypt() error = %v, wantErr %v", err, tt.wantErr) 159 | return 160 | } 161 | if !assert.Equal(t, tt.want, got) { 162 | t.Errorf("encrypt() = %v, want %v", got, tt.want) 163 | } 164 | }) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /cipher/nacl/private_key_encrypter_test.go: -------------------------------------------------------------------------------- 1 | package nacl 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "io" 7 | "testing" 8 | 9 | "github.com/mailchain/go-crypto" 10 | "github.com/mailchain/go-crypto/cipher" 11 | "github.com/mailchain/go-crypto/cipher/ecdh" 12 | "github.com/mailchain/go-crypto/ed25519/ed25519test" 13 | "github.com/mailchain/go-crypto/secp256k1/secp256k1test" 14 | "github.com/mailchain/go-crypto/sr25519/sr25519test" 15 | "github.com/mailchain/go-encoding/encodingtest" 16 | "github.com/stretchr/testify/assert" 17 | ) 18 | 19 | func TestNewPrivateKeyEncrypter(t *testing.T) { 20 | type args struct { 21 | privateKey crypto.PrivateKey 22 | } 23 | tests := []struct { 24 | name string 25 | args args 26 | want *PrivateKeyEncrypter 27 | assertion assert.ErrorAssertionFunc 28 | }{ 29 | { 30 | "secp256k1", 31 | args{ 32 | secp256k1test.AlicePrivateKey, 33 | }, 34 | &PrivateKeyEncrypter{ 35 | rand: rand.Reader, 36 | privateKey: secp256k1test.AlicePrivateKey, 37 | keyExchange: func() cipher.KeyExchange { 38 | k, _ := ecdh.NewSECP256K1(rand.Reader) 39 | return k 40 | }(), 41 | }, 42 | assert.NoError, 43 | }, 44 | { 45 | "ed25519", 46 | args{ 47 | ed25519test.AlicePrivateKey, 48 | }, 49 | &PrivateKeyEncrypter{ 50 | rand: rand.Reader, 51 | privateKey: ed25519test.AlicePrivateKey, 52 | keyExchange: func() cipher.KeyExchange { 53 | k, _ := ecdh.NewED25519(rand.Reader) 54 | return k 55 | }(), 56 | }, 57 | assert.NoError, 58 | }, 59 | { 60 | "sr25519", 61 | args{ 62 | sr25519test.AlicePrivateKey, 63 | }, 64 | &PrivateKeyEncrypter{ 65 | rand: rand.Reader, 66 | privateKey: sr25519test.AlicePrivateKey, 67 | keyExchange: func() cipher.KeyExchange { 68 | k, _ := ecdh.NewSR25519(rand.Reader) 69 | return k 70 | }(), 71 | }, 72 | assert.NoError, 73 | }, 74 | } 75 | for _, tt := range tests { 76 | t.Run(tt.name, func(t *testing.T) { 77 | got, err := NewPrivateKeyEncrypter(tt.args.privateKey) 78 | tt.assertion(t, err) 79 | assert.Equal(t, tt.want, got) 80 | }) 81 | } 82 | } 83 | 84 | func TestPrivateKeyEncrypter_Encrypt(t *testing.T) { 85 | type fields struct { 86 | rand io.Reader 87 | privateKey crypto.PrivateKey 88 | keyExchange cipher.KeyExchange 89 | } 90 | type args struct { 91 | message cipher.PlainContent 92 | } 93 | tests := []struct { 94 | name string 95 | fields fields 96 | args args 97 | want cipher.EncryptedContent 98 | assertion assert.ErrorAssertionFunc 99 | }{ 100 | { 101 | "secp256k1-alice", 102 | fields{ 103 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 104 | secp256k1test.AlicePrivateKey, 105 | func() cipher.KeyExchange { 106 | k, _ := ecdh.NewSECP256K1(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 107 | return k 108 | }(), 109 | }, 110 | args{ 111 | []byte("message"), 112 | }, 113 | encodingtest.MustDecodeHex("2be14142434445464748494a4b4c4d4e4f5051525354555657585ff8026ea550c27f5ec06e3ecdfb0850f3352400b7e9e2"), 114 | assert.NoError, 115 | }, 116 | { 117 | "secp256k1-bob", 118 | fields{ 119 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 120 | secp256k1test.BobPrivateKey, 121 | func() cipher.KeyExchange { 122 | k, _ := ecdh.NewSECP256K1(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 123 | return k 124 | }(), 125 | }, 126 | args{ 127 | []byte("message"), 128 | }, 129 | encodingtest.MustDecodeHex("2be14142434445464748494a4b4c4d4e4f5051525354555657583abb4c6b03073d8318a8edfa5e3820d761b9d07682e179"), 130 | assert.NoError, 131 | }, 132 | { 133 | "ed25519-alice", 134 | fields{ 135 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 136 | ed25519test.AlicePrivateKey, 137 | func() cipher.KeyExchange { 138 | k, _ := ecdh.NewED25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 139 | return k 140 | }(), 141 | }, 142 | args{ 143 | []byte("message"), 144 | }, 145 | encodingtest.MustDecodeHex("2be24142434445464748494a4b4c4d4e4f505152535455565758ede31931c34d9e1d251cf6466b1d628957a55bcce73486"), 146 | assert.NoError, 147 | }, 148 | { 149 | "ed25519-bob", 150 | fields{ 151 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 152 | ed25519test.BobPrivateKey, 153 | func() cipher.KeyExchange { 154 | k, _ := ecdh.NewED25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 155 | return k 156 | }(), 157 | }, 158 | args{ 159 | []byte("message"), 160 | }, 161 | encodingtest.MustDecodeHex("2be24142434445464748494a4b4c4d4e4f5051525354555657581a7d53c9fc1d9b4103f7e9c234f5897688cc68dbadbe17"), 162 | assert.NoError, 163 | }, 164 | { 165 | "sr25519-alice", 166 | fields{ 167 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 168 | sr25519test.AlicePrivateKey, 169 | func() cipher.KeyExchange { 170 | k, _ := ecdh.NewSR25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 171 | return k 172 | }(), 173 | }, 174 | args{ 175 | []byte("message"), 176 | }, 177 | nil, 178 | assert.Error, 179 | }, 180 | { 181 | "sr25519-bob", 182 | fields{ 183 | bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ")), 184 | sr25519test.BobPrivateKey, 185 | func() cipher.KeyExchange { 186 | k, _ := ecdh.NewSR25519(bytes.NewReader([]byte("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))) 187 | return k 188 | }(), 189 | }, 190 | args{ 191 | []byte("message"), 192 | }, 193 | nil, 194 | assert.Error, 195 | }, 196 | } 197 | for _, tt := range tests { 198 | t.Run(tt.name, func(t *testing.T) { 199 | e := PrivateKeyEncrypter{ 200 | rand: tt.fields.rand, 201 | privateKey: tt.fields.privateKey, 202 | keyExchange: tt.fields.keyExchange, 203 | } 204 | got, err := e.Encrypt(tt.args.message) 205 | tt.assertion(t, err) 206 | assert.Equal(t, tt.want, got) 207 | }) 208 | } 209 | } 210 | --------------------------------------------------------------------------------