Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
117
cloud/maplefile-backend/pkg/security/crypto/keys.go
Normal file
117
cloud/maplefile-backend/pkg/security/crypto/keys.go
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/awnumar/memguard"
|
||||
"github.com/tyler-smith/go-bip39"
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/nacl/box"
|
||||
)
|
||||
|
||||
// GenerateRandomKey generates a new random key using crypto_secretbox_keygen
|
||||
// JavaScript equivalent: sodium.randombytes_buf(crypto.MasterKeySize)
|
||||
func GenerateRandomKey(size int) ([]byte, error) {
|
||||
if size <= 0 {
|
||||
return nil, errors.New("key size must be positive")
|
||||
}
|
||||
|
||||
key := make([]byte, size)
|
||||
_, err := io.ReadFull(rand.Reader, key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate random key: %w", err)
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GenerateKeyPair generates a public/private key pair using NaCl box
|
||||
// JavaScript equivalent: sodium.crypto_box_keypair()
|
||||
func GenerateKeyPair() (publicKey, privateKey []byte, verificationID string, err error) {
|
||||
pubKey, privKey, err := box.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to generate key pair: %w", err)
|
||||
}
|
||||
|
||||
// Convert from fixed-size arrays to slices
|
||||
publicKey = pubKey[:]
|
||||
privateKey = privKey[:]
|
||||
|
||||
// Generate deterministic verification ID
|
||||
verificationID, err = GenerateVerificationID(publicKey[:])
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to generate verification ID: %w", err)
|
||||
}
|
||||
|
||||
return publicKey, privateKey, verificationID, nil
|
||||
}
|
||||
|
||||
// DeriveKeyFromPassword derives a key encryption key from a password using Argon2id
|
||||
// JavaScript equivalent: sodium.crypto_pwhash()
|
||||
// SECURITY: Password bytes are wiped from memory after key derivation.
|
||||
func DeriveKeyFromPassword(password string, salt []byte) ([]byte, error) {
|
||||
if len(salt) != Argon2SaltSize {
|
||||
return nil, fmt.Errorf("invalid salt size: expected %d, got %d", Argon2SaltSize, len(salt))
|
||||
}
|
||||
|
||||
// Convert password to bytes for wiping
|
||||
passwordBytes := []byte(password)
|
||||
defer memguard.WipeBytes(passwordBytes) // SECURITY: Wipe password bytes after use
|
||||
|
||||
// These parameters must match between Go and JavaScript
|
||||
key := argon2.IDKey(
|
||||
passwordBytes,
|
||||
salt,
|
||||
Argon2OpsLimit,
|
||||
Argon2MemLimit,
|
||||
Argon2Parallelism,
|
||||
Argon2KeySize,
|
||||
)
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// GenerateRandomNonce generates a random nonce for ChaCha20-Poly1305 encryption operations
|
||||
// JavaScript equivalent: sodium.randombytes_buf(crypto.NonceSize)
|
||||
func GenerateRandomNonce() ([]byte, error) {
|
||||
nonce := make([]byte, NonceSize) // NonceSize is now 12 for ChaCha20-Poly1305
|
||||
_, err := io.ReadFull(rand.Reader, nonce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate random nonce: %w", err)
|
||||
}
|
||||
return nonce, nil
|
||||
}
|
||||
|
||||
// GenerateVerificationID creates a human-readable representation of a public key
|
||||
// JavaScript equivalent: The same BIP39 mnemonic implementation
|
||||
// Generate VerificationID from public key (deterministic)
|
||||
func GenerateVerificationID(publicKey []byte) (string, error) {
|
||||
if len(publicKey) == 0 {
|
||||
return "", errors.New("public key cannot be empty")
|
||||
}
|
||||
|
||||
// 1. Hash the public key with SHA256
|
||||
hash := sha256.Sum256(publicKey)
|
||||
|
||||
// 2. Use the hash as entropy for BIP39
|
||||
mnemonic, err := bip39.NewMnemonic(hash[:])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate verification ID: %w", err)
|
||||
}
|
||||
|
||||
return mnemonic, nil
|
||||
}
|
||||
|
||||
// VerifyVerificationID checks if a verification ID matches a public key
|
||||
func VerifyVerificationID(publicKey []byte, verificationID string) bool {
|
||||
expectedID, err := GenerateVerificationID(publicKey)
|
||||
if err != nil {
|
||||
log.Printf("pkg.crypto.VerifyVerificationID - Failed to generate verification ID with error: %v\n", err)
|
||||
return false
|
||||
}
|
||||
return expectedID == verificationID
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue