// Open authenticates and recovers the message secured in the box. func Open(password, box []byte) (msg []byte, ok bool) { if len(box) < (secretbox.Overhead + 2*saltLength) { return } key, ok := recoverKey(password, box[:saltLength]) if !ok { return } ok = false tag := keccakMac(key[:saltLength], box[:saltLength]) if subtle.ConstantTimeCompare(tag, box[saltLength:2*saltLength]) != 1 { return } var sbKey [secretbox.KeySize]byte copy(sbKey[:], key[saltLength:]) msg, ok = secretbox.Open(box[2*saltLength:], &sbKey) secretbox.Zero(sbKey[:]) if !ok { msg = nil return } ok = true return }
// GenerateKey returns an appropriate private and public key pair // for securing messages. func GenerateKey() (priv *[PrivateKeySize]byte, pub *[PublicKeySize]byte, ok bool) { var priv1 [32]byte var priv2 [32]byte var pub1 [32]byte var pub2 [32]byte _, err := io.ReadFull(PRNG, priv1[:]) if err != nil { return } _, err = io.ReadFull(PRNG, priv2[:]) if err != nil { return } h := sha3.NewKeccak384() h.Write(priv1[:]) digest := h.Sum(nil) copy(priv1[:], digest) priv1[0] &= 248 priv1[31] &= 127 priv1[31] |= 64 h.Reset() h.Write(priv2[:]) digest = h.Sum(nil) copy(priv2[:], digest) priv2[0] &= 248 priv2[31] &= 127 priv2[31] |= 64 curve25519.ScalarBaseMult(&pub1, &priv1) curve25519.ScalarBaseMult(&pub2, &priv2) priv = new([PrivateKeySize]byte) copy(priv[:32], priv1[:]) copy(priv[32:], priv2[:]) pub = new([PublicKeySize]byte) copy(pub[:32], pub1[:]) copy(pub[32:], pub2[:]) secretbox.Zero(priv1[:]) secretbox.Zero(priv2[:]) ok = true return }
// SharedKey generates a secretbox key from a key exchange. Alice // receives Bob's public key, and uses her private key in conjunction // with Bob's public key to generate a suitable encryption key. In // practice, Alice actually receives an ephemeral public key from // Bob. This is handled automatically by Seal and Open, and should // only be used by protocols or applications that use a different key // exchange mechanism. func SharedKey(priv *[PrivateKeySize]byte, pub *[PublicKeySize]byte) *[secretbox.KeySize]byte { split := splitKeys(priv, pub) defer secretbox.Zero(split.priv1[:]) defer secretbox.Zero(split.priv2[:]) sk1 := sharedKey(&split.priv1, &split.pub1) sk2 := sharedKey(&split.priv2, &split.pub2) defer secretbox.Zero(sk1[:]) defer secretbox.Zero(sk2[:]) h := sha3.NewKeccak384() h.Write(sk2[:]) sk3 := h.Sum(nil) defer secretbox.Zero(sk3) var sk = new([secretbox.KeySize]byte) copy(sk[:32], sk1[:]) copy(sk[32:], sk3) return sk }
// Benchmark encryption of an 80-byte strongbox key. func BenchmarkEncrypt(b *testing.B) { b.StopTimer() sbKey, ok := secretbox.GenerateKey() if !ok { fmt.Println("tkdf: failed to generate secretbox secret key.") b.FailNow() } bmData = make([]byte, secretbox.KeySize) copy(bmData, sbKey[:]) secretbox.Zero(sbKey[:]) b.StartTimer() for i := 0; i < b.N; i++ { bmBox = Encrypt(testKey, bmData) if bmBox == nil { fmt.Println("tkdf: failed to encrypt benchmark") b.FailNow() } } }
// Seal secures a message using public key cryptography. It uses // ephemeral Curve25519 key pairs for encryption, but does not // digitally sign the box. func Seal(msg []byte, pub *[PublicKeySize]byte) (box []byte, ok bool) { epriv, epub, ok := GenerateKey() if !ok { return } sk := SharedKey(epriv, pub) defer secretbox.Zero(sk[:]) sbox, ok := secretbox.Seal(msg, sk) if !ok { return } box = make([]byte, len(sbox)+PublicKeySize+3) box[0] = BoxVersion box[1] = TypeSingle box[2] = CipherDualCurve25519 copy(box[3:PublicKeySize+3], epub[:]) copy(box[3+PublicKeySize:], sbox) return }
// Seal secures and authenticates the message using the password // to generate a key. func Seal(password, msg []byte) (box []byte, ok bool) { key, salt, ok := generateKey(password) if !ok { return } boxLength := len(msg) + secretbox.Overhead + 32 + saltLength box = make([]byte, boxLength) copy(box[:saltLength], salt) saltTag := keccakMac(key[:saltLength], salt) copy(box[saltLength:saltLength*2], saltTag) var sbKey [secretbox.KeySize]byte copy(sbKey[:], key[saltLength:]) sbox, ok := secretbox.Seal(msg, &sbKey) secretbox.Zero(sbKey[:]) if !ok { box = nil return } copy(box[2*saltLength:], sbox) ok = true return }
// Zero is a wrapper around the secretbox Zero function, while zeroises // its arguments. func Zero(in []byte) { secretbox.Zero(in) }