Skip to content

Commit 302bf36

Browse files
cpugopherbot
authored andcommitted
crypto/internal/fips140test: add AES ACVP tests
Adds ACVP AES test coverage for: * AES CBC * AES CTR * AES GCM (both internal & external iv gen) For AES key sizes of 128, 192, and 256 bits, based on the NIST spec: https://pages.nist.gov/ACVP/draft-celi-acvp-symmetric.html ECB mode is excluded based on upcoming policy changes forbidding its use. Internal IV gen is excluded from the go-acvp static test data since it's non-deterministic based on the DRBG. Updates #69642 Change-Id: I34f471725e2f1a2f5d32ab9877bde153abf2db0f Reviewed-on: https://go-review.googlesource.com/c/go/+/627655 Reviewed-by: Cherry Mui <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Filippo Valsorda <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]>
1 parent 273db12 commit 302bf36

File tree

3 files changed

+204
-2
lines changed

3 files changed

+204
-2
lines changed

src/crypto/internal/fips140test/acvp_capabilities.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,10 @@
4848
{"algorithm":"ECDSA","mode":"keyVer","revision":"FIPS186-5","curve":["P-224","P-256","P-384","P-521"]},
4949
{"algorithm":"ECDSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]},
5050
{"algorithm":"ECDSA","mode":"sigVer","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]},
51-
{"algorithm":"DetECDSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]}
51+
{"algorithm":"DetECDSA","mode":"sigGen","revision":"FIPS186-5","capabilities":[{"curve":["P-224","P-256","P-384","P-521"],"hashAlg":["SHA2-224","SHA2-256","SHA2-384","SHA2-512","SHA2-512/224","SHA2-512/256","SHA3-224","SHA3-256","SHA3-384","SHA3-512"]}]},
52+
53+
{"algorithm":"ACVP-AES-CBC","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"revision":"1.0"},
54+
{"algorithm":"ACVP-AES-CTR","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"payloadLen":[{"min":8,"max":128,"increment":8}],"incrementalCounter":true,"overflowCounter":true,"performCounterTests":true,"revision":"1.0"},
55+
{"algorithm":"ACVP-AES-GCM","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"payloadLen":[{"min":0,"max":65536,"increment":8}],"aadLen":[{"min":0,"max":65536,"increment":8}],"tagLen":[96,104,112,120,128],"ivLen":[96],"ivGen":"external","revision":"1.0"},
56+
{"algorithm":"ACVP-AES-GCM","direction":["encrypt","decrypt"],"keyLen":[128,192,256],"payloadLen":[{"min":0,"max":65536,"increment":8}],"aadLen":[{"min":0,"max":65536,"increment":8}],"tagLen":[128],"ivLen":[96],"ivGen":"internal","ivGenMode":"8.2.2","revision":"1.0"}
5257
]

src/crypto/internal/fips140test/acvp_test.config.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,9 @@
3131

3232
{"Wrapper": "go", "In": "vectors/EDDSA.bz2", "Out": "expected/EDDSA.bz2"},
3333

34-
{"Wrapper": "go", "In": "vectors/ECDSA.bz2", "Out": "expected/ECDSA.bz2"}
34+
{"Wrapper": "go", "In": "vectors/ECDSA.bz2", "Out": "expected/ECDSA.bz2"},
35+
36+
{"Wrapper": "go", "In": "vectors/ACVP-AES-CBC.bz2", "Out": "expected/ACVP-AES-CBC.bz2"},
37+
{"Wrapper": "go", "In": "vectors/ACVP-AES-CTR.bz2", "Out": "expected/ACVP-AES-CTR.bz2"},
38+
{"Wrapper": "go", "In": "vectors/ACVP-AES-GCM.bz2", "Out": "expected/ACVP-AES-GCM.bz2"}
3539
]

src/crypto/internal/fips140test/acvp_test.go

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"crypto/elliptic"
2525
"crypto/internal/cryptotest"
2626
"crypto/internal/fips140"
27+
"crypto/internal/fips140/aes"
28+
"crypto/internal/fips140/aes/gcm"
2729
"crypto/internal/fips140/ecdsa"
2830
"crypto/internal/fips140/ed25519"
2931
"crypto/internal/fips140/edwards25519"
@@ -82,6 +84,13 @@ const (
8284
ecdsaSigTypeDeterministic
8385
)
8486

87+
type aesDirection int
88+
89+
const (
90+
aesEncrypt aesDirection = iota
91+
aesDecrypt
92+
)
93+
8594
var (
8695
// SHA2 algorithm capabilities:
8796
// https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#section-7.2
@@ -97,6 +106,8 @@ var (
97106
// https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-7
98107
// ECDSA and DetECDSA algorithm capabilities:
99108
// https://pages.nist.gov/ACVP/draft-fussell-acvp-ecdsa.html#section-7
109+
// AES algorithm capabilities:
110+
// https://pages.nist.gov/ACVP/draft-celi-acvp-symmetric.html#section-7.3
100111
//go:embed acvp_capabilities.json
101112
capabilitiesJson []byte
102113

@@ -169,6 +180,15 @@ var (
169180
"ECDSA/sigGen": cmdEcdsaSigGenAft(ecdsaSigTypeNormal),
170181
"ECDSA/sigVer": cmdEcdsaSigVerAft(),
171182
"DetECDSA/sigGen": cmdEcdsaSigGenAft(ecdsaSigTypeDeterministic),
183+
184+
"AES-CBC/encrypt": cmdAesCbc(aesEncrypt),
185+
"AES-CBC/decrypt": cmdAesCbc(aesDecrypt),
186+
"AES-CTR/encrypt": cmdAesCtr(aesEncrypt),
187+
"AES-CTR/decrypt": cmdAesCtr(aesDecrypt),
188+
"AES-GCM/seal": cmdAesGcmSeal(false),
189+
"AES-GCM/open": cmdAesGcmOpen(false),
190+
"AES-GCM-randnonce/seal": cmdAesGcmSeal(true),
191+
"AES-GCM-randnonce/open": cmdAesGcmOpen(true),
172192
}
173193
)
174194

@@ -961,6 +981,179 @@ func lookupCurve(name string) (elliptic.Curve, error) {
961981
return c, nil
962982
}
963983

984+
func cmdAesCbc(direction aesDirection) command {
985+
return command{
986+
requiredArgs: 4, // Key, ciphertext or plaintext, IV, num iterations
987+
handler: func(args [][]byte) ([][]byte, error) {
988+
if direction != aesEncrypt && direction != aesDecrypt {
989+
panic("invalid AES direction")
990+
}
991+
992+
key := args[0]
993+
input := args[1]
994+
iv := args[2]
995+
numIterations := binary.LittleEndian.Uint32(args[3])
996+
997+
blockCipher, err := aes.New(key)
998+
if err != nil {
999+
return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err)
1000+
}
1001+
1002+
if len(input)%blockCipher.BlockSize() != 0 || len(input) == 0 {
1003+
return nil, fmt.Errorf("invalid ciphertext/plaintext size %d: not a multiple of block size %d",
1004+
len(input), blockCipher.BlockSize())
1005+
}
1006+
1007+
if blockCipher.BlockSize() != len(iv) {
1008+
return nil, fmt.Errorf("invalid IV size: expected %d, got %d", blockCipher.BlockSize(), len(iv))
1009+
}
1010+
1011+
result := make([]byte, len(input))
1012+
prevResult := make([]byte, len(input))
1013+
prevInput := make([]byte, len(input))
1014+
1015+
for i := uint32(0); i < numIterations; i++ {
1016+
copy(prevResult, result)
1017+
1018+
if i > 0 {
1019+
if direction == aesEncrypt {
1020+
copy(iv, result)
1021+
} else {
1022+
copy(iv, prevInput)
1023+
}
1024+
}
1025+
1026+
if direction == aesEncrypt {
1027+
cbcEnc := aes.NewCBCEncrypter(blockCipher, [16]byte(iv))
1028+
cbcEnc.CryptBlocks(result, input)
1029+
} else {
1030+
cbcDec := aes.NewCBCDecrypter(blockCipher, [16]byte(iv))
1031+
cbcDec.CryptBlocks(result, input)
1032+
}
1033+
1034+
if direction == aesDecrypt {
1035+
copy(prevInput, input)
1036+
}
1037+
1038+
if i == 0 {
1039+
copy(input, iv)
1040+
} else {
1041+
copy(input, prevResult)
1042+
}
1043+
}
1044+
1045+
return [][]byte{result, prevResult}, nil
1046+
},
1047+
}
1048+
}
1049+
1050+
func cmdAesCtr(direction aesDirection) command {
1051+
return command{
1052+
requiredArgs: 4, // Key, ciphertext or plaintext, initial counter, num iterations (constant 1)
1053+
handler: func(args [][]byte) ([][]byte, error) {
1054+
if direction != aesEncrypt && direction != aesDecrypt {
1055+
panic("invalid AES direction")
1056+
}
1057+
1058+
key := args[0]
1059+
input := args[1]
1060+
iv := args[2]
1061+
numIterations := binary.LittleEndian.Uint32(args[3])
1062+
if numIterations != 1 {
1063+
return nil, fmt.Errorf("invalid num iterations: expected 1, got %d", numIterations)
1064+
}
1065+
1066+
if len(iv) != aes.BlockSize {
1067+
return nil, fmt.Errorf("invalid IV size: expected %d, got %d", aes.BlockSize, len(iv))
1068+
}
1069+
1070+
blockCipher, err := aes.New(key)
1071+
if err != nil {
1072+
return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err)
1073+
}
1074+
1075+
result := make([]byte, len(input))
1076+
stream := aes.NewCTR(blockCipher, iv)
1077+
stream.XORKeyStream(result, input)
1078+
1079+
return [][]byte{result}, nil
1080+
},
1081+
}
1082+
}
1083+
1084+
func cmdAesGcmSeal(randNonce bool) command {
1085+
return command{
1086+
requiredArgs: 5, // tag len, key, plaintext, nonce (empty for randNonce), additional data
1087+
handler: func(args [][]byte) ([][]byte, error) {
1088+
tagLen := binary.LittleEndian.Uint32(args[0])
1089+
key := args[1]
1090+
plaintext := args[2]
1091+
nonce := args[3]
1092+
additionalData := args[4]
1093+
1094+
blockCipher, err := aes.New(key)
1095+
if err != nil {
1096+
return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err)
1097+
}
1098+
1099+
aesGCM, err := gcm.New(blockCipher, 12, int(tagLen))
1100+
if err != nil {
1101+
return nil, fmt.Errorf("creating AES-GCM with tag len %d: %w", tagLen, err)
1102+
}
1103+
1104+
var ct []byte
1105+
if !randNonce {
1106+
ct = aesGCM.Seal(nil, nonce, plaintext, additionalData)
1107+
} else {
1108+
var internalNonce [12]byte
1109+
ct = make([]byte, len(plaintext)+16)
1110+
gcm.SealWithRandomNonce(aesGCM, internalNonce[:], ct, plaintext, additionalData)
1111+
// acvptool expects the internally generated nonce to be appended to the end of the ciphertext.
1112+
ct = append(ct, internalNonce[:]...)
1113+
}
1114+
1115+
return [][]byte{ct}, nil
1116+
},
1117+
}
1118+
}
1119+
1120+
func cmdAesGcmOpen(randNonce bool) command {
1121+
return command{
1122+
requiredArgs: 5, // tag len, key, ciphertext, nonce (empty for randNonce), additional data
1123+
handler: func(args [][]byte) ([][]byte, error) {
1124+
1125+
tagLen := binary.LittleEndian.Uint32(args[0])
1126+
key := args[1]
1127+
ciphertext := args[2]
1128+
nonce := args[3]
1129+
additionalData := args[4]
1130+
1131+
blockCipher, err := aes.New(key)
1132+
if err != nil {
1133+
return nil, fmt.Errorf("creating AES block cipher with key len %d: %w", len(key), err)
1134+
}
1135+
1136+
aesGCM, err := gcm.New(blockCipher, 12, int(tagLen))
1137+
if err != nil {
1138+
return nil, fmt.Errorf("creating AES-GCM with tag len %d: %w", tagLen, err)
1139+
}
1140+
1141+
if randNonce {
1142+
// for randNonce tests acvptool appends the nonce to the end of the ciphertext.
1143+
nonce = ciphertext[len(ciphertext)-12:]
1144+
ciphertext = ciphertext[:len(ciphertext)-12]
1145+
}
1146+
1147+
pt, err := aesGCM.Open(nil, nonce, ciphertext, additionalData)
1148+
if err != nil {
1149+
return [][]byte{{0}, nil}, nil
1150+
}
1151+
1152+
return [][]byte{{1}, pt}, nil
1153+
},
1154+
}
1155+
}
1156+
9641157
func TestACVP(t *testing.T) {
9651158
testenv.SkipIfShortAndSlow(t)
9661159

0 commit comments

Comments
 (0)