git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/crypto/curve25519.go (about)

     1  package crypto
     2  
     3  import (
     4  	"golang.org/x/crypto/blake2b"
     5  	"golang.org/x/crypto/curve25519"
     6  )
     7  
     8  const (
     9  	// Curve25519PublicKeySize is the size, in bytes, of public keys as used in this package.
    10  	Curve25519PublicKeySize = curve25519.PointSize
    11  
    12  	// Curve25519PrivateKeySize is the size, in bytes, of private keys as used in this package.
    13  	Curve25519PrivateKeySize = curve25519.ScalarSize
    14  
    15  	// X25519SharedSecretSize is the size, in bytes of the shared secret of a x25519 key exchange
    16  	X25519SharedSecretSize = 32
    17  )
    18  
    19  // GenerateCurve25519KeyPair generates a public/private Curve25519 key pair
    20  func GenerateCurve25519KeyPair() (publicKey Curve25519PublicKey, privateKey Curve25519PrivateKey, err error) {
    21  	privateKey, err = RandBytes(Curve25519PrivateKeySize)
    22  	if err != nil {
    23  		return
    24  	}
    25  
    26  	publicKey, err = privateKey.Public()
    27  	return
    28  }
    29  
    30  // Curve25519PublicKey is the type of Curve25519 public keys.
    31  type Curve25519PublicKey []byte
    32  
    33  // KeyExchange performs a x25519 key exchange with the given private key
    34  func (publicKey Curve25519PublicKey) KeyExchange(privateKey Curve25519PrivateKey) (sharedSecret []byte, err error) {
    35  	sharedSecret, err = curve25519.X25519(privateKey, publicKey)
    36  	return
    37  }
    38  
    39  // Encrypt performs a x25519 key exchange, and encrypt the message using `XChaCha20-Poly1305` with
    40  // the shared secret as key and nonce as nonce.
    41  func (publicKey Curve25519PublicKey) Encrypt(fromPrivateKey Curve25519PrivateKey, nonce []byte,
    42  	message []byte) (ciphertext []byte, err error) {
    43  	sharedSecret, err := publicKey.KeyExchange(fromPrivateKey)
    44  	defer Zeroize(sharedSecret)
    45  	if err != nil {
    46  		return
    47  	}
    48  
    49  	cipher, err := NewAEAD(sharedSecret)
    50  	if err != nil {
    51  		return
    52  	}
    53  
    54  	ciphertext = cipher.Seal(nil, nonce, message, nil)
    55  	return
    56  }
    57  
    58  // EncryptEphemeral generates an ephemeral Curve25519KeyPair and `Encrypt` message using the public key,
    59  // the ephemeral privateKey and `blake2b(size=AEADNonceSize, message=ephemeralPublicKey || publicKey)` as nonce
    60  func (publicKey Curve25519PublicKey) EncryptEphemeral(message []byte) (ciphertext []byte, ephemeralPublicKey Curve25519PublicKey, err error) {
    61  	ephemeralPublicKey, ephemeralPrivateKey, err := GenerateCurve25519KeyPair()
    62  	defer Zeroize(ephemeralPrivateKey)
    63  	if err != nil {
    64  		return
    65  	}
    66  
    67  	nonce, err := generateNonce(ephemeralPublicKey, publicKey)
    68  	if err != nil {
    69  		return
    70  	}
    71  
    72  	ciphertext, err = publicKey.Encrypt(ephemeralPrivateKey, nonce, message)
    73  	return
    74  }
    75  
    76  func generateNonce(ephemeralPublicKey, publicKey Curve25519PublicKey) (nonce []byte, err error) {
    77  	var nonceMessage []byte
    78  
    79  	nonceMessage = append(nonceMessage, []byte(ephemeralPublicKey)...)
    80  	nonceMessage = append(nonceMessage, []byte(publicKey)...)
    81  	hash, err := blake2b.New(AEADNonceSize, nil)
    82  	if err != nil {
    83  		return
    84  	}
    85  	hash.Write(nonceMessage)
    86  	nonce = hash.Sum(nil)
    87  	return
    88  }
    89  
    90  // Curve25519PrivateKey is the type of Curve25519 private keys.
    91  type Curve25519PrivateKey []byte
    92  
    93  // Public returns the Curve25519PublicKey corresponding to privateKey.
    94  func (privateKey Curve25519PrivateKey) Public() (publicKey Curve25519PublicKey, err error) {
    95  	publicKey, err = curve25519.X25519(privateKey, curve25519.Basepoint)
    96  	return
    97  }
    98  
    99  // KeyExchange performs a x25519 key exchange with the given public key
   100  func (privateKey Curve25519PrivateKey) KeyExchange(publicKey Curve25519PublicKey) (sharedSecret []byte, err error) {
   101  	sharedSecret, err = curve25519.X25519(privateKey, publicKey)
   102  	return
   103  }
   104  
   105  // Decrypt performs a x25519 key exchange, and decrypt the ciphertext using `XChaCha20-Poly1305` with
   106  // the shared secret as key and nonce as nonce.
   107  func (privateKey Curve25519PrivateKey) Decrypt(fromPublicKey Curve25519PublicKey, nonce []byte, ciphertext []byte) (plaintext []byte, err error) {
   108  	sharedSecret, err := privateKey.KeyExchange(fromPublicKey)
   109  	defer Zeroize(sharedSecret)
   110  	if err != nil {
   111  		return
   112  	}
   113  
   114  	cipher, err := NewAEAD(sharedSecret)
   115  	if err != nil {
   116  		return
   117  	}
   118  
   119  	plaintext, err = cipher.Open(nil, nonce, ciphertext, nil)
   120  	return
   121  }
   122  
   123  // DecryptEphemeral generates a noce with `blake2b(size=AEADNonceSize, message=ephemeralPublicKey || privateKey.PublicKey())`
   124  // and decrypt the `ciphertext` using `Decrypt`
   125  func (privateKey Curve25519PrivateKey) DecryptEphemeral(ephemeralPublicKey Curve25519PublicKey, ciphertext []byte) (plaintext []byte, err error) {
   126  	myPublicKey, err := privateKey.Public()
   127  	if err != nil {
   128  		return
   129  	}
   130  
   131  	nonce, err := generateNonce(ephemeralPublicKey, myPublicKey)
   132  	if err != nil {
   133  		return
   134  	}
   135  
   136  	return privateKey.Decrypt(ephemeralPublicKey, nonce, ciphertext)
   137  }