add vendoring
This commit is contained in:
124
vendor/maunium.net/go/mautrix/crypto/ssss/key.go
generated
vendored
Normal file
124
vendor/maunium.net/go/mautrix/crypto/ssss/key.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2020 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package ssss
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"maunium.net/go/mautrix/crypto/utils"
|
||||
)
|
||||
|
||||
// Key represents a SSSS private key and related metadata.
|
||||
type Key struct {
|
||||
ID string `json:"-"`
|
||||
Key []byte `json:"-"`
|
||||
Metadata *KeyMetadata `json:"-"`
|
||||
}
|
||||
|
||||
// NewKey generates a new SSSS key, optionally based on the given passphrase.
|
||||
//
|
||||
// Errors are only returned if crypto/rand runs out of randomness.
|
||||
func NewKey(passphrase string) (*Key, error) {
|
||||
// We don't support any other algorithms currently.
|
||||
keyData := KeyMetadata{Algorithm: AlgorithmAESHMACSHA2}
|
||||
|
||||
var ssssKey []byte
|
||||
if len(passphrase) > 0 {
|
||||
// There's a passphrase. We need to generate a salt for it, set the metadata
|
||||
// and then compute the key using the passphrase and the metadata.
|
||||
saltBytes := make([]byte, 24)
|
||||
if _, err := rand.Read(saltBytes); err != nil {
|
||||
return nil, fmt.Errorf("failed to get random bytes for salt: %w", err)
|
||||
}
|
||||
keyData.Passphrase = &PassphraseMetadata{
|
||||
Algorithm: PassphraseAlgorithmPBKDF2,
|
||||
Iterations: 500000,
|
||||
Salt: base64.StdEncoding.EncodeToString(saltBytes),
|
||||
Bits: 256,
|
||||
}
|
||||
var err error
|
||||
ssssKey, err = keyData.Passphrase.GetKey(passphrase)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get key from passphrase: %w", err)
|
||||
}
|
||||
} else {
|
||||
// No passphrase, just generate a random key
|
||||
ssssKey = make([]byte, 32)
|
||||
if _, err := rand.Read(ssssKey); err != nil {
|
||||
return nil, fmt.Errorf("failed to get random bytes for key: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a random ID for the key. It's what identifies the key in account data.
|
||||
keyIDBytes := make([]byte, 24)
|
||||
if _, err := rand.Read(keyIDBytes); err != nil {
|
||||
return nil, fmt.Errorf("failed to get random bytes for key ID: %w", err)
|
||||
}
|
||||
|
||||
// We store a certain hash in the key metadata so that clients can check if the user entered the correct key.
|
||||
var ivBytes [utils.AESCTRIVLength]byte
|
||||
if _, err := rand.Read(ivBytes[:]); err != nil {
|
||||
return nil, fmt.Errorf("failed to get random bytes for IV: %w", err)
|
||||
}
|
||||
keyData.IV = base64.StdEncoding.EncodeToString(ivBytes[:])
|
||||
keyData.MAC = keyData.calculateHash(ssssKey)
|
||||
|
||||
return &Key{
|
||||
Key: ssssKey,
|
||||
ID: base64.StdEncoding.EncodeToString(keyIDBytes),
|
||||
Metadata: &keyData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RecoveryKey gets the recovery key for this SSSS key.
|
||||
func (key *Key) RecoveryKey() string {
|
||||
return utils.EncodeBase58RecoveryKey(key.Key)
|
||||
}
|
||||
|
||||
// Encrypt encrypts the given data with this key.
|
||||
func (key *Key) Encrypt(eventType string, data []byte) EncryptedKeyData {
|
||||
aesKey, hmacKey := utils.DeriveKeysSHA256(key.Key, eventType)
|
||||
|
||||
iv := utils.GenA256CTRIV()
|
||||
payload := make([]byte, base64.StdEncoding.EncodedLen(len(data)))
|
||||
base64.StdEncoding.Encode(payload, data)
|
||||
utils.XorA256CTR(payload, aesKey, iv)
|
||||
|
||||
return EncryptedKeyData{
|
||||
Ciphertext: base64.StdEncoding.EncodeToString(payload),
|
||||
IV: base64.StdEncoding.EncodeToString(iv[:]),
|
||||
MAC: utils.HMACSHA256B64(payload, hmacKey),
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given encrypted data with this key.
|
||||
func (key *Key) Decrypt(eventType string, data EncryptedKeyData) ([]byte, error) {
|
||||
var ivBytes [utils.AESCTRIVLength]byte
|
||||
decodedIV, _ := base64.StdEncoding.DecodeString(data.IV)
|
||||
copy(ivBytes[:], decodedIV)
|
||||
|
||||
payload, err := base64.StdEncoding.DecodeString(data.Ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// derive the AES and HMAC keys for the requested event type using the SSSS key
|
||||
aesKey, hmacKey := utils.DeriveKeysSHA256(key.Key, eventType)
|
||||
|
||||
// compare the stored MAC with the one we calculated from the ciphertext
|
||||
calcMac := utils.HMACSHA256B64(payload, hmacKey)
|
||||
if strings.ReplaceAll(data.MAC, "=", "") != strings.ReplaceAll(calcMac, "=", "") {
|
||||
return nil, ErrKeyDataMACMismatch
|
||||
}
|
||||
|
||||
utils.XorA256CTR(payload, aesKey, ivBytes)
|
||||
decryptedDecoded, err := base64.StdEncoding.DecodeString(string(payload))
|
||||
return decryptedDecoded, err
|
||||
}
|
||||
Reference in New Issue
Block a user