mirror of
https://mau.dev/mautrix/go.git
synced 2026-03-14 14:25:53 +01:00
Generate and upload SSSS keys with random key or passphrase and store SSSS keys in Olm machine
Signed-off-by: Nikos Filippakis <me@nfil.dev>
This commit is contained in:
parent
0949fe63d1
commit
6159b22f94
7 changed files with 226 additions and 83 deletions
21
client.go
21
client.go
|
|
@ -604,27 +604,6 @@ func (cli *Client) SetAccountData(name string, data interface{}) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetAccountData gets the user's account data of this type. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-user-userid-account-data-type
|
||||
func (cli *Client) GetAccountData(name string) (data map[string]interface{}, err error) {
|
||||
urlPath := cli.BuildURL("user", cli.UserID, "account_data", name)
|
||||
s := make(map[string]interface{})
|
||||
|
||||
_, err = cli.MakeRequest("GET", urlPath, nil, &s)
|
||||
|
||||
return s, err
|
||||
}
|
||||
|
||||
// SetAccountData sets the user's account data of this type. See https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-user-userid-account-data-type
|
||||
func (cli *Client) SetAccountData(name string, data map[string]interface{}) (err error) {
|
||||
urlPath := cli.BuildURL("user", cli.UserID, "account_data", name)
|
||||
_, err = cli.MakeRequest("PUT", urlPath, &data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ReqSendEvent struct {
|
||||
Timestamp int64
|
||||
TransactionID string
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ type EncryptedFile struct {
|
|||
}
|
||||
|
||||
func NewEncryptedFile() *EncryptedFile {
|
||||
key, iv := utils.GenA256CTR()
|
||||
key, iv := utils.GenAttachmentA256CTR()
|
||||
return &EncryptedFile{
|
||||
Key: JSONWebKey{
|
||||
Key: base64.RawURLEncoding.EncodeToString(key[:]),
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ type OlmMachine struct {
|
|||
|
||||
roomKeyRequestFilled *sync.Map
|
||||
keyVerificationTransactionState *sync.Map
|
||||
|
||||
crossSigningKeys *CrossSigningKeysCache
|
||||
}
|
||||
|
||||
// StateStore is used by OlmMachine to get room state information that's needed for encryption.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ import (
|
|||
// PkSigning stores a key pair for signing messages.
|
||||
type PkSigning struct {
|
||||
int *C.OlmPkSigning
|
||||
PublicKey []byte
|
||||
PublicKey string
|
||||
Seed []byte
|
||||
}
|
||||
|
||||
func pkSigningSize() uint {
|
||||
|
|
@ -44,24 +45,31 @@ func (p *PkSigning) Clear() {
|
|||
C.olm_clear_pk_signing((*C.OlmPkSigning)(p.int))
|
||||
}
|
||||
|
||||
// NewPkSigning creates a new PkSigning object, containing a key pair for signing messages.
|
||||
func NewPkSigning() (*PkSigning, error) {
|
||||
// NewPkSigningFromSeed creates a new PkSigning object using the given seed.
|
||||
func NewPkSigningFromSeed(seed []byte) (*PkSigning, error) {
|
||||
p := newBlackPkSigning()
|
||||
p.Clear()
|
||||
pubKey := make([]byte, pkSigningPublicKeyLength())
|
||||
// Make the slice be at least length 1
|
||||
random := make([]byte, pkSigningSeedLength())
|
||||
_, err := rand.Read(random)
|
||||
if C.olm_pk_signing_key_from_seed((*C.OlmPkSigning)(p.int),
|
||||
unsafe.Pointer(&pubKey[0]), C.size_t(len(pubKey)),
|
||||
unsafe.Pointer(&seed[0]), C.size_t(len(seed))) == errorVal() {
|
||||
return nil, p.lastError()
|
||||
}
|
||||
p.PublicKey = string(pubKey)
|
||||
p.Seed = seed
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewPkSigning creates a new PkSigning object, containing a key pair for signing messages.
|
||||
func NewPkSigning() (*PkSigning, error) {
|
||||
// Generate the seed
|
||||
seed := make([]byte, pkSigningSeedLength())
|
||||
_, err := rand.Read(seed)
|
||||
if err != nil {
|
||||
panic(NotEnoughGoRandom)
|
||||
}
|
||||
if C.olm_pk_signing_key_from_seed((*C.OlmPkSigning)(p.int),
|
||||
unsafe.Pointer(&pubKey[0]), C.size_t(len(pubKey)),
|
||||
unsafe.Pointer(&random[0]), C.size_t(len(random))) == errorVal() {
|
||||
return nil, p.lastError()
|
||||
}
|
||||
p.PublicKey = pubKey
|
||||
return p, nil
|
||||
pk, err := NewPkSigningFromSeed(seed)
|
||||
return pk, err
|
||||
}
|
||||
|
||||
// Sign creates a signature for the given message using this key.
|
||||
|
|
|
|||
232
crypto/ssss.go
232
crypto/ssss.go
|
|
@ -7,11 +7,12 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"maunium.net/go/mautrix/crypto/olm"
|
||||
"maunium.net/go/mautrix/crypto/utils"
|
||||
)
|
||||
|
||||
|
|
@ -33,12 +34,19 @@ var AccountDataSelfSigningKeyType AccountDataKeyType = "m.cross_signing.self_sig
|
|||
// SSSSAlgorithm is the type for algorithms used in SSSS.
|
||||
type SSSSAlgorithm string
|
||||
|
||||
// SSSSAlgorithmPBKDF2 is the algorithm used for deriving a key from a SSSS passphrase.
|
||||
// SSSSAlgorithmPBKDF2 is the algorithm used for deriving a key from an SSSS passphrase.
|
||||
var SSSSAlgorithmPBKDF2 SSSSAlgorithm = "m.pbkdf2"
|
||||
|
||||
// SSSSAlgorithmAESHMACSHA2 is the algorithm used for encrypting and verifying secrets stored on SSSS.
|
||||
var SSSSAlgorithmAESHMACSHA2 SSSSAlgorithm = "m.secret_storage.v1.aes-hmac-sha2"
|
||||
|
||||
// CrossSigningKeysCache holds the three cross-signing keys for the current user.
|
||||
type CrossSigningKeysCache struct {
|
||||
MasterKey *olm.PkSigning
|
||||
SelfSigningKey *olm.PkSigning
|
||||
UserSigningKey *olm.PkSigning
|
||||
}
|
||||
|
||||
type ssssPassphraseData struct {
|
||||
Algorithm SSSSAlgorithm `json:"algorithm"`
|
||||
Iterations int `json:"iterations"`
|
||||
|
|
@ -63,8 +71,10 @@ type ssssEncryptedData struct {
|
|||
Encrypted map[string]ssssEncryptedKeyData `json:"encrypted"`
|
||||
}
|
||||
|
||||
// getDefaultKeyID retrieves the default key ID for this account from SSSS.
|
||||
func (mach *OlmMachine) getDefaultKeyID() (string, error) {
|
||||
data, err := mach.Client.GetAccountData(string(AccountDataDefaultKeyType))
|
||||
var data map[string]string
|
||||
err := mach.Client.GetAccountData(string(AccountDataDefaultKeyType), &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -72,24 +82,19 @@ func (mach *OlmMachine) getDefaultKeyID() (string, error) {
|
|||
if !ok {
|
||||
return "", errors.New("Could not get default key ID")
|
||||
}
|
||||
return keyID.(string), nil
|
||||
return keyID, nil
|
||||
}
|
||||
|
||||
func (mach *OlmMachine) retrieveDecryptSSSSKey(keyName AccountDataKeyType, keyID string, ssssKey []byte) ([utils.AESCTRKeyLength]byte, error) {
|
||||
// retrieveDecryptXSigningKey retrieves the requested cross-signing key from SSSS and decrypts it using the given SSSS key.
|
||||
func (mach *OlmMachine) retrieveDecryptXSigningKey(keyName AccountDataKeyType, keyID string, ssssKey []byte) ([utils.AESCTRKeyLength]byte, error) {
|
||||
var decryptedKey [utils.AESCTRKeyLength]byte
|
||||
var encData ssssEncryptedData
|
||||
data, err := mach.Client.GetAccountData(string(keyName))
|
||||
if err != nil {
|
||||
return decryptedKey, err
|
||||
}
|
||||
|
||||
bytes, err := json.Marshal(data)
|
||||
// retrieve and parse the account data for this key type from SSSS
|
||||
err := mach.Client.GetAccountData(string(keyName), &encData)
|
||||
if err != nil {
|
||||
return decryptedKey, err
|
||||
}
|
||||
if err := json.Unmarshal(bytes, &encData); err != nil {
|
||||
return decryptedKey, err
|
||||
}
|
||||
|
||||
keyEncData, ok := encData.Encrypted[keyID]
|
||||
if !ok {
|
||||
|
|
@ -105,19 +110,27 @@ func (mach *OlmMachine) retrieveDecryptSSSSKey(keyName AccountDataKeyType, keyID
|
|||
return decryptedKey, err
|
||||
}
|
||||
|
||||
// derive the AES and HMAC keys for the requested cross-signing key type using the SSSS key
|
||||
aesKey, hmacKey := utils.DeriveKeysSHA256(ssssKey, string(keyName))
|
||||
|
||||
// compare the stored MAC with the one we calculated from the ciphertext
|
||||
calcMac := utils.HMACSHA256B64(ciphertextBytes, hmacKey)
|
||||
if strings.ReplaceAll(keyEncData.MAC, "=", "") != strings.ReplaceAll(calcMac, "=", "") {
|
||||
return decryptedKey, errors.New("Key data MAC mismatch")
|
||||
}
|
||||
|
||||
// use the derived AES key to decrypt the requested cross-signing key seed
|
||||
decrypted := utils.XorA256CTR(ciphertextBytes, aesKey, ivBytes)
|
||||
copy(decryptedKey[:], decrypted)
|
||||
decryptedDecoded, err := base64.StdEncoding.DecodeString(string(decrypted))
|
||||
if err != nil {
|
||||
return decryptedKey, err
|
||||
}
|
||||
copy(decryptedKey[:], decryptedDecoded)
|
||||
|
||||
return decryptedKey, nil
|
||||
}
|
||||
|
||||
// retrieveSSSSKeyData retrieves the data for the requested key from SSSS.
|
||||
func (mach *OlmMachine) retrieveSSSSKeyData(keyID string) (*ssssKeyData, error) {
|
||||
var keyData ssssKeyData
|
||||
data, err := mach.Client.GetAccountData("m.secret_storage.key." + keyID)
|
||||
|
|
@ -136,6 +149,7 @@ func (mach *OlmMachine) retrieveSSSSKeyData(keyID string) (*ssssKeyData, error)
|
|||
return &keyData, nil
|
||||
}
|
||||
|
||||
// verifySSSSKey verifies the SSSS key is valid by calculating and comparing its MAC.
|
||||
func verifySSSSKey(ssssKey []byte, iv, mac string) error {
|
||||
aesKey, hmacKey := utils.DeriveKeysSHA256(ssssKey, "")
|
||||
|
||||
|
|
@ -155,6 +169,39 @@ func verifySSSSKey(ssssKey []byte, iv, mac string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// keysCacheFromSSSSKey retrieves all the cross-signing keys from SSSS using the given SSSS key and stores them in the olm machine.
|
||||
func (mach *OlmMachine) keysCacheFromSSSSKey(keyID string, ssssKey []byte) error {
|
||||
masterKey, err := mach.retrieveDecryptXSigningKey(AccountDataMasterKeyType, keyID, ssssKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selfSignKey, err := mach.retrieveDecryptXSigningKey(AccountDataSelfSigningKeyType, keyID, ssssKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userSignKey, err := mach.retrieveDecryptXSigningKey(AccountDataUserSigningKeyType, keyID, ssssKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var keysCache CrossSigningKeysCache
|
||||
if keysCache.MasterKey, err = olm.NewPkSigningFromSeed(masterKey[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if keysCache.SelfSigningKey, err = olm.NewPkSigningFromSeed(selfSignKey[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if keysCache.UserSigningKey, err = olm.NewPkSigningFromSeed(userSignKey[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mach.Log.Trace("Retrieved keys from SSSS: Master `%v` Self-signing `%v` User-signing `%v`",
|
||||
keysCache.MasterKey.PublicKey, keysCache.SelfSigningKey.PublicKey, keysCache.UserSigningKey.PublicKey)
|
||||
|
||||
mach.crossSigningKeys = &keysCache
|
||||
return nil
|
||||
}
|
||||
|
||||
// RetrieveCrossSigningKeysWithPassphrase retrieves the cross-signing keys from SSSS using the given passphrase to decrypt them.
|
||||
func (mach *OlmMachine) RetrieveCrossSigningKeysWithPassphrase(passphrase string) error {
|
||||
keyID, err := mach.getDefaultKeyID()
|
||||
|
|
@ -189,22 +236,7 @@ func (mach *OlmMachine) RetrieveCrossSigningKeysWithPassphrase(passphrase string
|
|||
|
||||
mach.Log.Debug("Retrieved and verified SSSS key from passphrase")
|
||||
|
||||
masterKey, err := mach.retrieveDecryptSSSSKey(AccountDataMasterKeyType, keyID, ssssKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selfSignKey, err := mach.retrieveDecryptSSSSKey(AccountDataSelfSigningKeyType, keyID, ssssKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userSignKey, err := mach.retrieveDecryptSSSSKey(AccountDataUserSigningKeyType, keyID, ssssKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mach.Log.Error("keys %v %v %v", masterKey, selfSignKey, userSignKey)
|
||||
|
||||
return nil
|
||||
return mach.keysCacheFromSSSSKey(keyID, ssssKey)
|
||||
}
|
||||
|
||||
// RetrieveCrossSigningKeysWithRecoveryKey retrieves the cross-signing keys from SSSS using the given recovery key to decrypt them.
|
||||
|
|
@ -221,27 +253,141 @@ func (mach *OlmMachine) RetrieveCrossSigningKeysWithRecoveryKey(recoveryKey stri
|
|||
}
|
||||
|
||||
ssssKey := utils.DecodeBase58RecoveryKey(recoveryKey)
|
||||
if ssssKey == nil {
|
||||
return errors.New("Error decoding recovery key")
|
||||
}
|
||||
|
||||
if err := verifySSSSKey(ssssKey[:], keyData.IV, keyData.MAC); err != nil {
|
||||
if err := verifySSSSKey(ssssKey, keyData.IV, keyData.MAC); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mach.Log.Debug("Retrieved and verified SSSS key from recovery key")
|
||||
|
||||
masterKey, err := mach.retrieveDecryptSSSSKey(AccountDataMasterKeyType, keyID, ssssKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
selfSignKey, err := mach.retrieveDecryptSSSSKey(AccountDataSelfSigningKeyType, keyID, ssssKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userSignKey, err := mach.retrieveDecryptSSSSKey(AccountDataUserSigningKeyType, keyID, ssssKey[:])
|
||||
if err != nil {
|
||||
return err
|
||||
return mach.keysCacheFromSSSSKey(keyID, ssssKey)
|
||||
}
|
||||
|
||||
// GenerateAndUploadCrossSigningKeys generates a new key with all corresponding cross-signing keys.
|
||||
// A passphrase can optionally be given for generating the SSSS key, otherwise a random key is used.
|
||||
// The recovery key for retrieving the SSSS key is returned.
|
||||
func (mach *OlmMachine) GenerateAndUploadCrossSigningKeys(passphrase ...string) (string, error) {
|
||||
var ssssKey []byte
|
||||
newKeyData := ssssKeyData{Algorithm: SSSSAlgorithmAESHMACSHA2}
|
||||
|
||||
if len(passphrase) > 0 {
|
||||
// if a passphrase is given use it to generate an SSSS key and save the parameters used for PBKDF2
|
||||
var saltBytes [24]byte
|
||||
if _, err := rand.Read(saltBytes[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
passData := ssssPassphraseData{
|
||||
Algorithm: SSSSAlgorithmPBKDF2,
|
||||
Iterations: 500000,
|
||||
Bits: 256,
|
||||
Salt: base64.StdEncoding.EncodeToString(saltBytes[:]),
|
||||
}
|
||||
newKeyData.Passphrase = &passData
|
||||
|
||||
mach.Log.Debug("Generating SSSS key from passphrase")
|
||||
ssssKey = utils.PBKDF2SHA512([]byte(passphrase[0]), []byte(passData.Salt), passData.Iterations, passData.Bits)
|
||||
} else {
|
||||
// if no passphrase generate a random SSSS key
|
||||
mach.Log.Debug("Generating random SSSS key")
|
||||
ssssKey = make([]byte, 32)
|
||||
if _, err := rand.Read(ssssKey); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
mach.Log.Error("keys %v %v %v", masterKey, selfSignKey, userSignKey)
|
||||
var ivBytes [utils.AESCTRIVLength]byte
|
||||
if _, err := rand.Read(ivBytes[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// derive the AES and HMAC key for generating the SSSS key's MAC to be uploaded
|
||||
aesKey, hmacKey := utils.DeriveKeysSHA256(ssssKey, "")
|
||||
|
||||
var zeroBytes [utils.AESCTRKeyLength]byte
|
||||
cipher := utils.XorA256CTR(zeroBytes[:], aesKey, ivBytes)
|
||||
|
||||
newKeyData.MAC = utils.HMACSHA256B64(cipher, hmacKey)
|
||||
newKeyData.IV = base64.StdEncoding.EncodeToString(ivBytes[:])
|
||||
mach.Log.Debug("Calculated MAC for AES key: `%v`", newKeyData.MAC)
|
||||
|
||||
// generate the three cross-signing keys
|
||||
var keysCache CrossSigningKeysCache
|
||||
var err error
|
||||
if keysCache.MasterKey, err = olm.NewPkSigning(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if keysCache.SelfSigningKey, err = olm.NewPkSigning(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if keysCache.UserSigningKey, err = olm.NewPkSigning(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
mach.Log.Debug("Generated keys: Master: `%v` Self-signing: `%v` User-signing: `%v`",
|
||||
keysCache.MasterKey.PublicKey, keysCache.SelfSigningKey.PublicKey, keysCache.UserSigningKey.PublicKey)
|
||||
|
||||
// generate a key ID for this SSSS key and store the SSSS key info
|
||||
var genKeyIDBytes [24]byte
|
||||
if _, err := rand.Read(genKeyIDBytes[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
genKeyID := base64.StdEncoding.EncodeToString(genKeyIDBytes[:])
|
||||
mach.Log.Debug("Generated SSSS key ID: `%v`", genKeyID)
|
||||
if err := mach.Client.SetAccountData("m.secret_storage.key."+genKeyID, newKeyData); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// upload the three cross-signing keys
|
||||
if err := mach.uploadCrossSigningKeys(genKeyID, ssssKey, &keysCache, true); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// save cross-signing keys, generate and return recovery key
|
||||
mach.crossSigningKeys = &keysCache
|
||||
return utils.EncodeBase58RecoveryKey(ssssKey), nil
|
||||
}
|
||||
|
||||
// uploadCrossSigningKeys stores the given cross-signing keys to SSSS under the given key ID,
|
||||
// optionally setting the key as the default one.
|
||||
func (mach *OlmMachine) uploadCrossSigningKeys(keyID string, ssssKey []byte, keys *CrossSigningKeysCache, setDefaultKey bool) error {
|
||||
if setDefaultKey {
|
||||
mach.Client.SetAccountData(string(AccountDataDefaultKeyType), map[string]interface{}{"key": keyID})
|
||||
}
|
||||
if err := mach.uploadCrossSigningSingleKey(keyID, AccountDataMasterKeyType, keys.MasterKey, ssssKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mach.uploadCrossSigningSingleKey(keyID, AccountDataSelfSigningKeyType, keys.SelfSigningKey, ssssKey); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := mach.uploadCrossSigningSingleKey(keyID, AccountDataUserSigningKeyType, keys.UserSigningKey, ssssKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// uploadCrossSigningSingleKey encrypts and uploads a single cross-signing key to SSSS using the given key.
|
||||
func (mach *OlmMachine) uploadCrossSigningSingleKey(keyID string, keyName AccountDataKeyType, pk *olm.PkSigning, ssssKey []byte) error {
|
||||
aesKey, hmacKey := utils.DeriveKeysSHA256(ssssKey, string(keyName))
|
||||
|
||||
iv := utils.GenA256CTRIV()
|
||||
plaintextEncoded := base64.StdEncoding.EncodeToString(pk.Seed)
|
||||
encrypted := utils.XorA256CTR([]byte(plaintextEncoded), aesKey, iv)
|
||||
macStr := utils.HMACSHA256B64(encrypted, hmacKey)
|
||||
ivStr := base64.StdEncoding.EncodeToString(iv[:])
|
||||
|
||||
mach.Log.Debug("Calculated MAC for key %v with ID `%v`: `%v`", keyName, keyID, macStr)
|
||||
|
||||
encryptedXKeyData := ssssEncryptedData{
|
||||
Encrypted: map[string]ssssEncryptedKeyData{
|
||||
keyID: {
|
||||
Ciphertext: base64.StdEncoding.EncodeToString(encrypted),
|
||||
IV: ivStr,
|
||||
MAC: macStr,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return mach.Client.SetAccountData(string(keyName), encryptedXKeyData)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ func XorA256CTR(source []byte, key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byt
|
|||
return result
|
||||
}
|
||||
|
||||
// GenA256CTR generates a new random AES256-CTR key and IV.
|
||||
func GenA256CTR() (key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) {
|
||||
// GenAttachmentA256CTR generates a new random AES256-CTR key and IV suitable for encrypting attachments.
|
||||
func GenAttachmentA256CTR() (key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) {
|
||||
_, err := rand.Read(key[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -55,11 +55,21 @@ func GenA256CTR() (key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) {
|
|||
return
|
||||
}
|
||||
|
||||
// GenA256CTRIV generates a random IV for AES256-CTR with the last bit set to zero.
|
||||
func GenA256CTRIV() (iv [AESCTRIVLength]byte) {
|
||||
_, err := rand.Read(iv[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
iv[8] &= 0x7F
|
||||
return
|
||||
}
|
||||
|
||||
// DeriveKeysSHA256 derives an AES and a HMAC key from the given recovery key.
|
||||
func DeriveKeysSHA256(key []byte, name string) ([AESCTRKeyLength]byte, [HMACKeyLength]byte) {
|
||||
var zeroBytes [32]byte
|
||||
|
||||
derivedHkdf := hkdf.New(sha256.New, key, zeroBytes[:], []byte(name))
|
||||
derivedHkdf := hkdf.New(sha256.New, key[:], zeroBytes[:], []byte(name))
|
||||
|
||||
var aesKey [AESCTRKeyLength]byte
|
||||
var hmacKey [HMACKeyLength]byte
|
||||
|
|
@ -75,26 +85,24 @@ func PBKDF2SHA512(password []byte, salt []byte, iters int, keyLenBits int) []byt
|
|||
}
|
||||
|
||||
// DecodeBase58RecoveryKey recovers the secret storage from a recovery key.
|
||||
func DecodeBase58RecoveryKey(recoveryKey string) [AESCTRKeyLength]byte {
|
||||
var res [AESCTRKeyLength]byte
|
||||
func DecodeBase58RecoveryKey(recoveryKey string) []byte {
|
||||
noSpaces := strings.ReplaceAll(recoveryKey, " ", "")
|
||||
decoded := base58.Decode(noSpaces)
|
||||
if len(decoded) != AESCTRKeyLength+3 { // AESCTRKeyLength bytes key and 3 bytes prefix / parity
|
||||
return res
|
||||
return nil
|
||||
}
|
||||
var parity byte
|
||||
for _, b := range decoded[:34] {
|
||||
parity ^= b
|
||||
}
|
||||
if parity != decoded[34] || decoded[0] != 0x8B || decoded[1] != 1 {
|
||||
return res
|
||||
return nil
|
||||
}
|
||||
copy(res[:], decoded[2:34])
|
||||
return res
|
||||
return decoded[2:34]
|
||||
}
|
||||
|
||||
// EncodeBase58RecoveryKey recovers the secret storage from a recovery key.
|
||||
func EncodeBase58RecoveryKey(key [AESCTRKeyLength]byte) string {
|
||||
func EncodeBase58RecoveryKey(key []byte) string {
|
||||
var inputBytes [35]byte
|
||||
copy(inputBytes[2:34], key[:])
|
||||
inputBytes[0] = 0x8B
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
func TestAES256Ctr(t *testing.T) {
|
||||
expected := "Hello world"
|
||||
key, iv := GenA256CTR()
|
||||
key, iv := GenAttachmentA256CTR()
|
||||
enc := XorA256CTR([]byte(expected), key, iv)
|
||||
dec := XorA256CTR(enc, key, iv)
|
||||
if string(dec) != expected {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue