// Copyright 2012 Aaron Jacobs. All Rights Reserved. // Author: aaronjjacobs@gmail.com (Aaron Jacobs) // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package siv import ( "crypto/aes" "crypto/cipher" "crypto/subtle" "fmt" ) // *NotAuthenticError is returned by Decrypt if the input is otherwise // well-formed but the ciphertext doesn't check out as authentic. This could be // due to an incorrect key, corrupted ciphertext, or incorrect/corrupted // associated data. type NotAuthenticError struct { s string } func (e *NotAuthenticError) Error() string { return e.s } // Given ciphertext previously generated by Encrypt and the key and associated // data that were used when generating the ciphertext, return the original // plaintext given to Encrypt. If the input is well-formed but the key is // incorrect, return an instance of WrongKeyError. func Decrypt(key, ciphertext []byte, associated [][]byte) ([]byte, error) { keyLen := len(key) associatedLen := len(associated) // The first 16 bytes of the ciphertext are the SIV. if len(ciphertext) < aes.BlockSize { return nil, fmt.Errorf("Invalid ciphertext; length must be at least 16.") } v := ciphertext[0:aes.BlockSize] c := ciphertext[aes.BlockSize:] // Make sure the key length is legal. switch keyLen { case 32, 48, 64: default: return nil, fmt.Errorf("SIV requires a 32-, 48-, or 64-byte key.") } // Derive subkeys. k1 := key[:keyLen/2] k2 := key[keyLen/2:] // Make sure the number of associated data is legal, per RFC 5297 section 7. if associatedLen > 126 { return nil, fmt.Errorf("len(associated) may be no more than 126.") } // Create a CTR cipher using a version of v with the 31st and 63rd bits // zeroed out. q := dup(v) q[aes.BlockSize-4] &= 0x7f q[aes.BlockSize-8] &= 0x7f ciph, err := aes.NewCipher(k2) if err != nil { return nil, fmt.Errorf("aes.NewCipher: %v", err) } ctrCiph := cipher.NewCTR(ciph, q) // Decrypt the ciphertext. plaintext := make([]byte, len(c)) ctrCiph.XORKeyStream(plaintext, c) // Verify the SIV. s2vStrings := make([][]byte, associatedLen+1) copy(s2vStrings, associated) s2vStrings[associatedLen] = plaintext t := s2v(k1, s2vStrings, nil) if len(t) != aes.BlockSize { panic(fmt.Sprintf("Unexpected output of S2V: %v", t)) } if subtle.ConstantTimeCompare(t, v) != 1 { return nil, &NotAuthenticError{ "Couldn't validate the authenticity of the ciphertext and " + "associated data."} } return plaintext, nil }