ephermeral keypair, ratchets, shared secret and more
This commit is contained in:
@@ -24,16 +24,17 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
CURVE = "Curve25519"
|
CURVE = "Curve25519"
|
||||||
KEYSIZE = 512 // 256*2 bits
|
KEYSIZE = 512 // Combined length of encryption key (256) and signing key (256)
|
||||||
RATCHETSIZE = 256 // bits
|
RATCHETSIZE = 256
|
||||||
RATCHET_EXPIRY = 2592000 // 60*60*24*30 seconds (30 days)
|
RATCHET_EXPIRY = 2592000 // 30 days in seconds
|
||||||
TRUNCATED_HASHLENGTH = 128 // bits
|
TRUNCATED_HASHLENGTH = 128
|
||||||
NAME_HASH_LENGTH = 80 // bits
|
NAME_HASH_LENGTH = 80
|
||||||
|
|
||||||
TOKEN_OVERHEAD = 16 // AES block size in bytes
|
// Token constants for Fernet-like spec
|
||||||
AES128_BLOCKSIZE = 16 // bytes
|
TOKEN_OVERHEAD = 16 // AES block size
|
||||||
HASHLENGTH = 256 // bits
|
AES128_BLOCKSIZE = 16
|
||||||
SIGLENGTH = KEYSIZE // bits
|
HASHLENGTH = 256
|
||||||
|
SIGLENGTH = KEYSIZE
|
||||||
)
|
)
|
||||||
|
|
||||||
type Identity struct {
|
type Identity struct {
|
||||||
@@ -209,61 +210,72 @@ func (i *Identity) Encrypt(plaintext []byte, ratchet []byte) ([]byte, error) {
|
|||||||
return nil, errors.New("encryption failed: identity does not hold a public key")
|
return nil, errors.New("encryption failed: identity does not hold a public key")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate ephemeral key pair
|
// Generate ephemeral keypair
|
||||||
ephemeralKey, err := curve25519.X25519(make([]byte, 32), curve25519.Basepoint)
|
ephemeralPrivKey := make([]byte, curve25519.ScalarSize)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, ephemeralPrivKey); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ephemeralPubKey, err := curve25519.X25519(ephemeralPrivKey, curve25519.Basepoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
ephemeralPubBytes := ephemeralKey
|
|
||||||
|
|
||||||
var targetPublicKey []byte
|
// Use ratchet key if provided, otherwise use identity public key
|
||||||
|
targetKey := i.publicKey
|
||||||
if ratchet != nil {
|
if ratchet != nil {
|
||||||
targetPublicKey = ratchet
|
targetKey = ratchet
|
||||||
} else {
|
|
||||||
targetPublicKey = i.publicKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate shared key
|
// Generate shared secret
|
||||||
sharedKey, err := curve25519.X25519(ephemeralKey, targetPublicKey)
|
sharedSecret, err := curve25519.X25519(ephemeralPrivKey, targetKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Derive encryption key using HKDF
|
// Derive encryption key using HKDF
|
||||||
derivedKey := hkdf.New(sha256.New, sharedKey, i.hash, nil)
|
kdf := hkdf.New(sha256.New, sharedSecret, i.GetSalt(), i.GetContext())
|
||||||
key := make([]byte, 32)
|
key := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(derivedKey, key); err != nil {
|
if _, err := io.ReadFull(kdf, key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt using AES-CBC
|
// Encrypt using AES-128-CBC with PKCS7 padding
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key[:16]) // Use AES-128
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add PKCS7 padding
|
||||||
|
padding := aes.BlockSize - len(plaintext)%aes.BlockSize
|
||||||
|
padtext := make([]byte, len(plaintext)+padding)
|
||||||
|
copy(padtext, plaintext)
|
||||||
|
for i := len(plaintext); i < len(padtext); i++ {
|
||||||
|
padtext[i] = byte(padding)
|
||||||
|
}
|
||||||
|
|
||||||
// Generate IV
|
// Generate IV
|
||||||
iv := make([]byte, aes.BlockSize)
|
iv := make([]byte, aes.BlockSize)
|
||||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pad plaintext
|
|
||||||
padding := aes.BlockSize - len(plaintext)%aes.BlockSize
|
|
||||||
padtext := make([]byte, len(plaintext)+padding)
|
|
||||||
copy(padtext, plaintext)
|
|
||||||
for i := len(plaintext); i < len(padtext); i++ {
|
|
||||||
padtext[i] = byte(padding)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt
|
// Encrypt
|
||||||
mode := cipher.NewCBCEncrypter(block, iv)
|
mode := cipher.NewCBCEncrypter(block, iv)
|
||||||
ciphertext := make([]byte, len(padtext))
|
ciphertext := make([]byte, len(padtext))
|
||||||
mode.CryptBlocks(ciphertext, padtext)
|
mode.CryptBlocks(ciphertext, padtext)
|
||||||
|
|
||||||
// Combine ephemeral public key + IV + ciphertext
|
// Calculate HMAC
|
||||||
token := append(ephemeralPubBytes, iv...)
|
h := hmac.New(sha256.New, key)
|
||||||
|
h.Write(append(ephemeralPubKey, append(iv, ciphertext...)...))
|
||||||
|
mac := h.Sum(nil)
|
||||||
|
|
||||||
|
// Combine all components into final token
|
||||||
|
token := make([]byte, 0, len(ephemeralPubKey)+len(iv)+len(ciphertext)+len(mac))
|
||||||
|
token = append(token, ephemeralPubKey...)
|
||||||
|
token = append(token, iv...)
|
||||||
token = append(token, ciphertext...)
|
token = append(token, ciphertext...)
|
||||||
|
token = append(token, mac...)
|
||||||
|
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user