// CalcSharedSecret does a triple DH from the keypacks to generate the shared secret. myKeys needs to contain private keys, peerKeys only needs public keys // Sending determines if one is sender or recipient of a message. func CalcSharedSecret(myKeys, peerKeys *KeyPack, nonce *[NonceSize]byte, sending bool) (sharedSecret [SharedKeySize]byte) { preKey := make([]byte, 3*Curve25519KeySize+NonceSize) // three keys plus nonce key1, key2, key3 := new(Curve25519Key), new(Curve25519Key), new(Curve25519Key) log.Secretf("TemporaryPrivKey: %x\n", *myKeys.TemporaryPrivKey) log.Secretf("ConstantPrivKey: %x\n", *myKeys.ConstantPrivKey) log.Secretf("TemporaryPubKey: %x\n", *peerKeys.TemporaryPubKey) log.Secretf("ConstantPubKey: %x\n", *peerKeys.ConstantPubKey) scalarMult(key1, myKeys.TemporaryPrivKey, peerKeys.TemporaryPubKey) log.Secretf("Key1: %x\n", *key1) scalarMult(key2, myKeys.ConstantPrivKey, peerKeys.TemporaryPubKey) scalarMult(key3, myKeys.TemporaryPrivKey, peerKeys.ConstantPubKey) preKey = append(preKey, key1[:]...) if sending { log.Secretf("Key2: %x\n", *key2) log.Secretf("Key3: %x\n", *key3) preKey = append(preKey, key2[:]...) preKey = append(preKey, key3[:]...) } else { // Swap for receiver log.Secretf("Key2: %x\n", *key3) log.Secretf("Key3: %x\n", *key2) preKey = append(preKey, key3[:]...) preKey = append(preKey, key2[:]...) } log.Secretf("Nonce: %x\n", *nonce) preKey = append(preKey, nonce[:]...) return sha512.Sum512(preKey) }
// RePad adds the deterministic padding to a message. func RePad(msg []byte, padKey *[PadKeySize]byte, totalLength int) []byte { msgLen := len(msg) padLen := totalLength - msgLen log.Secretf("RePad Length: %d\n", padLen) if padLen <= 0 { return msg } log.Secretf("RePad Key: %x\n", *padKey) ret := append(make([]byte, 0), msg[:msgLen-HMACSize]...) // Message body ret = append(ret, GenPad(padKey, padLen)...) // Deterministic padding ret = append(ret, msg[msgLen-HMACSize:]...) // HMAC return ret }
// DecryptBody decrypts a body and verifies the hmac. func (bd *DecryptBodyDef) DecryptBody(data []byte) ([]byte, byte, error) { var content []byte log.Secretf("Shared Secret: %x\n", bd.SharedSecret) hmacKey, symmetricKey := CalcKeys(bd.SharedSecret) log.Secretf("HMAC Key: %x\n", *hmacKey) log.Secretf("Symmetric Key: %x\n", *symmetricKey) hmaccalc := hmac.New(sha256.New, hmacKey[:]) hmaccalc.Write(data[:len(data)-HMACSize]) hmaccalcSum := hmaccalc.Sum(nil) if !hmac.Equal(hmaccalcSum, data[len(data)-HMACSize:]) { log.Debugf("Bad HMAC: %x\n", hmaccalcSum) return nil, 0, ErrBadHMAC } blockcipher, err := aes.NewCipher(symmetricKey[:]) if err != nil { return nil, 0, err } ctr := cipher.NewCTR(blockcipher, bd.IV[:aes.BlockSize]) // Calculate size of first read. Read one block unless message is too short firstRead := aes.BlockSize if len(data)-HMACSize < aes.BlockSize { firstRead = len(data) } header := make([]byte, firstRead) ctr.XORKeyStream(header, data[:firstRead]) msgType := header[0] // Decode Message Type length := binary.BigEndian.Uint16(header[1:encryptedHeaderSize]) // Decode Message Length log.Secretf("Real Length: %d\n", length) // Length escapes one block (minus header which is not part of the length) if length > aes.BlockSize-encryptedHeaderSize { // We have already read aes.BlockSize, but it includes the header nlength := length - aes.BlockSize + encryptedHeaderSize content = make([]byte, nlength) // Decrypt whatever is left ctr.XORKeyStream(content, data[aes.BlockSize:aes.BlockSize+nlength]) } // Concat both reads content = append(header, content...) // Only return after header til end of message return content[encryptedHeaderSize : length+encryptedHeaderSize], msgType, nil }
// EncryptBody takes data and creates a body out of it. func (bd *EncryptBodyDef) EncryptBody(data []byte) (*EncryptedBody, error) { var detSize int dataLen := len(data) log.Secretf("Data Length: %d\n", dataLen) if dataLen > bd.TotalLength-HMACSize-encryptedHeaderSize || dataLen > BodyMaxLength { // HMac is appended, one byte for type, two for length return nil, ErrTooLong } encryptedHeader := make([]byte, aes.BlockSize) log.Secretf("Shared Secret: %x\n", bd.SharedSecret) hmacKey, symmetricKey := CalcKeys(bd.SharedSecret) log.Secretf("HMAC Key: %x\n", *hmacKey) log.Secretf("Symmetric Key: %x\n", *symmetricKey) eBody := new(EncryptedBody) eBody.Encrypted = make([]byte, dataLen+encryptedHeaderSize) blockcipher, err := aes.NewCipher(symmetricKey[:]) if err != nil { return nil, err } ctr := cipher.NewCTR(blockcipher, bd.IV[:aes.BlockSize]) log.Secretf("Encrypt IV: %x\n", bd.IV[:aes.BlockSize]) encryptedHeader[0] = bd.MessageType binary.BigEndian.PutUint16(encryptedHeader[1:encryptedHeaderSize], uint16(dataLen)) // Length for data if len(data) > aes.BlockSize-encryptedHeaderSize { // Multiple blocks. Encrypted header first, then remainder copy(encryptedHeader[encryptedHeaderSize:], data[0:aes.BlockSize-encryptedHeaderSize]) ctr.XORKeyStream(eBody.Encrypted[0:aes.BlockSize], encryptedHeader) ctr.XORKeyStream(eBody.Encrypted[aes.BlockSize:], data[aes.BlockSize-encryptedHeaderSize:]) } else { // Short block. Only one encryption copy(encryptedHeader[encryptedHeaderSize:], data) ctr.XORKeyStream(eBody.Encrypted, encryptedHeader[:len(eBody.Encrypted)]) } hmaccalc := hmac.New(sha256.New, hmacKey[:]) hmaccalc.Write(eBody.Encrypted) detSize = dataLen + encryptedHeaderSize + HMACSize if bd.PadToLength-detSize > 0 { randomPadKey, err := GenPadKey() if err != nil { return nil, err } padlength := bd.PadToLength - detSize log.Secretf("Random Pad Key: %x\n", *randomPadKey) log.Secretf("Random Pad Length: %d\n", padlength) eBody.RandomPadding = GenPad(randomPadKey, padlength) detSize = bd.PadToLength hmaccalc.Write(eBody.RandomPadding) } if bd.TotalLength > detSize { detPadKey, err := GenPadKey() if err != nil { return nil, err } padlength := bd.TotalLength - detSize log.Secretf("Deterministic Pad Key: %x\n", *detPadKey) log.Secretf("Deterministic Pad Length: %d\n", padlength) eBody.PadKey = *detPadKey eBody.DeterministicPadding = GenPad(detPadKey, bd.TotalLength-detSize) hmaccalc.Write(eBody.DeterministicPadding) } copy(eBody.HMACSum[:], hmaccalc.Sum(nil)) return eBody, nil }