// Decrypt a message encrypted for a particular anonymity set. // Returns the cleartext message on success, or an error on failure. // // The caller provides the anonymity set for which the message is intended, // and the private key corresponding to one of the public keys in the set. // Decrypt verifies that the message is encrypted correctly for this set - // in particular, that it could be decrypted by ALL of the listed members - // before returning successfully with the decrypted message. // This verification ensures that a malicious sender // cannot de-anonymize a receiver by constructing a ciphertext incorrectly // so as to be decryptable by only some members of the set. // As a side-effect, this verification also ensures plaintext-awareness: // that is, it is infeasible for a sender to construct any ciphertext // that will be accepted by the receiver without knowing the plaintext. // func Decrypt(suite abstract.Suite, ciphertext []byte, anonymitySet Set, mine int, privateKey abstract.Secret, hide bool) ([]byte, error) { // Decrypt and check the encrypted key-header. xb, hdrlen, err := decryptKey(suite, ciphertext, anonymitySet, mine, privateKey, hide) if err != nil { return nil, err } // Determine the message layout cipher := suite.Cipher(xb) maclen := cipher.KeySize() if len(ciphertext) < hdrlen+maclen { return nil, errors.New("ciphertext too short") } hdrhi := hdrlen msghi := len(ciphertext) - maclen // Decrypt the message and check the MAC ctx := ciphertext[hdrhi:msghi] mac := ciphertext[msghi:] msg := make([]byte, len(ctx)) cipher.Message(msg, ctx, ctx) cipher.Partial(mac, mac, nil) if subtle.ConstantTimeAllEq(mac, 0) == 0 { return nil, errors.New("invalid ciphertext: failed MAC check") } return msg, nil }
// Open decrypts and authenticates a message encrypted using Seal. // It decrypts sealed message src and appends it onto plaintext buffer dst, // growing the dst buffer if it is too small (or nil), // and returns the resulting destination buffer or an error. // func (c Cipher) Open(dst, src []byte) ([]byte, error) { m := c.KeySize() l := len(src) - m if l < 0 { return nil, errors.New("sealed ciphertext too short") } ctx := src[:l] mac := src[l:] dst, msg := util.Grow(dst, l) if &msg[0] != &ctx[0] { // Decrypt and absorb ciphertext c.Message(msg, ctx, ctx) } else { tmp := make([]byte, l) c.Message(tmp, ctx, ctx) copy(msg, tmp) } c.Message(mac, mac, nil) // Compute MAC and XOR with received if subtle.ConstantTimeAllEq(mac, 0) == 0 { return nil, errors.New("ciphertext authentication failed") } return dst, nil }
func (ca *cipherAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) { // Fork off a temporary Cipher state indexed via the nonce ct := ca.Clone() ct.Message(nil, nil, nonce) // Compute the plaintext's length authl := ct.KeySize() plainl := len(ciphertext) - authl if plainl < 0 { return nil, errors.New("AEAD ciphertext too short") } auth := ciphertext[plainl:] ciphertext = ciphertext[:plainl] // Decrypt the plaintext and update the temporary Cipher state dst, plaintext := util.Grow(dst, plainl) ct.Message(plaintext, ciphertext, ciphertext) // Compute and check the authenticator based on post-encryption state ct.Message(auth, auth, nil) if subtle.ConstantTimeAllEq(auth, 0) == 0 { return nil, errors.New("AEAD authenticator check failed") } return dst, nil }
// Tests a Cipher: // 1) Encryption / decryption work // 2) Encryption / decryption with different key don't work // 3) Changing a bit in the ciphertext or mac results in failed mac check // 4) Different keys produce sufficiently random output func TestAuthenticateAndEncrypt(t *testing.T, newCipher func([]byte, ...interface{}) abstract.Cipher, n int, bitdiff float64, text []byte) { cryptsize := len(text) decrypted := make([]byte, len(text)) bc := newCipher(nil) keysize := bc.KeySize() hashsize := bc.HashSize() mac := make([]byte, hashsize) ncrypts := make([][]byte, n) nkeys := make([][]byte, n) nmacs := make([][]byte, n) // Encrypt / decrypt / mac test for i := range nkeys { nkeys[i] = make([]byte, keysize) rand.Read(nkeys[i]) bc = newCipher(nkeys[i]) ncrypts[i] = make([]byte, cryptsize) bc.Message(ncrypts[i], text, ncrypts[i]) nmacs[i] = make([]byte, hashsize) bc.Message(nmacs[i], nil, nil) bc = newCipher(nkeys[i]) bc.Message(decrypted, ncrypts[i], ncrypts[i]) if !bytes.Equal(text, decrypted) { t.Log("Encryption / Decryption failed", i) t.FailNow() } bc.Message(mac, nmacs[i], nil) if subtle.ConstantTimeAllEq(mac, 0) != 1 { t.Log("MAC Check failed") t.FailNow() } } // Different keys test for i := range ncrypts { for j := range ncrypts { if i == j { continue } bc = newCipher(nkeys[i]) bc.Message(decrypted, ncrypts[j], ncrypts[j]) bc.Message(mac, nmacs[j], nil) if subtle.ConstantTimeAllEq(mac, 0) == 1 { t.Log("MAC Check passed") t.FailNow() } } } // Not enough randomness in 1 byte to pass this consistently if len(ncrypts[0]) < 8 { return } // Bit difference test for i := range ncrypts { for j := i + 1; j < len(ncrypts); j++ { res := BitDiff(ncrypts[i], ncrypts[j]) if res < bitdiff { t.Log("Encryptions not sufficiently different", res) t.FailNow() } } } deltacopy := make([]byte, cryptsize) // Bits in either testmsg or testmac should be flipped // then the resulting MAC check should fail deltatest := func(index int, testmsg []byte, testmac []byte) { bc = newCipher(nkeys[index]) bc.Message(decrypted, testmsg, testmsg) bc.Message(mac, testmac, nil) if subtle.ConstantTimeAllEq(mac, 0) == 1 { t.Log("MAC Check passed") t.FailNow() } } for i := range ncrypts { copy(ncrypts[i], deltacopy) deltacopy[0] ^= 255 deltatest(i, deltacopy, nmacs[i]) deltacopy[0] = ncrypts[i][0] deltacopy[len(deltacopy)/2-1] ^= 255 deltatest(i, deltacopy, nmacs[i]) deltacopy[len(deltacopy)/2-1] = ncrypts[i][len(deltacopy)/2-1] deltacopy[len(deltacopy)-1] ^= 255 deltatest(i, deltacopy, nmacs[i]) deltacopy[len(deltacopy)-1] = ncrypts[i][len(deltacopy)-1] deltamac := make([]byte, hashsize) copy(nmacs[i], deltamac) deltamac[0] ^= 255 deltatest(i, ncrypts[i], deltamac) } }