Skip to content

Commit 343be17

Browse files
authored
Add ACVP Support for KTS-IFC (#3009)
### Issues: Addresses P355857148 ### Description of changes: This PR adds ACVP support for `KTS-IFC KTS-OAEP-basic` ### Call-outs: Due to the random nature of RSA-OAEP encryption, no expected vector was added. To review, please run the script locally to verify the result. ### Testing: Run `check_expected.go` on the new KTS-IFC test vector. By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.
1 parent ec37c27 commit 343be17

5 files changed

Lines changed: 249 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR ISC
3+
4+
package subprocess
5+
6+
import (
7+
"encoding/json"
8+
"fmt"
9+
)
10+
11+
type ktsVectorSet struct {
12+
Groups []ktsTestGroup `json:"testGroups"`
13+
}
14+
15+
type ktsTestGroup struct {
16+
ID uint64 `json:"tgId"`
17+
Type string `json:"testType"`
18+
Role string `json:"kasRole"`
19+
Scheme string `json:"scheme"`
20+
KtsConfiguration ktsConfiguration `json:"ktsConfiguration"`
21+
Tests []ktsTest `json:"tests"`
22+
L uint32 `json:"l"`
23+
}
24+
25+
type ktsConfiguration struct {
26+
HashAlg string `json:"hashAlg"`
27+
}
28+
29+
type ktsTest struct {
30+
ID uint64 `json:"tcId"`
31+
ServerN hexEncodedByteString `json:"serverN"`
32+
ServerE hexEncodedByteString `json:"serverE"`
33+
IutN hexEncodedByteString `json:"iutN"`
34+
IutE hexEncodedByteString `json:"iutE"`
35+
IutP hexEncodedByteString `json:"iutP"`
36+
IutQ hexEncodedByteString `json:"iutQ"`
37+
IutD hexEncodedByteString `json:"iutD"`
38+
Ct hexEncodedByteString `json:"serverC"`
39+
}
40+
41+
type ktsTestGroupResponse struct {
42+
ID uint64 `json:"tgId"`
43+
Tests []ktsTestResponse `json:"tests"`
44+
}
45+
46+
type ktsTestResponse struct {
47+
ID uint64 `json:"tcId"`
48+
IutC hexEncodedByteString `json:"iutC,omitempty"`
49+
Dkm hexEncodedByteString `json:"dkm"`
50+
}
51+
52+
type kts struct{}
53+
54+
func (k *kts) Process(vectorSet []byte, m Transactable) (interface{}, error) {
55+
var parsed ktsVectorSet
56+
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
57+
return nil, err
58+
}
59+
60+
// See https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ifc.html
61+
var ret []ktsTestGroupResponse
62+
for _, group := range parsed.Groups {
63+
response := ktsTestGroupResponse{
64+
ID: group.ID,
65+
}
66+
67+
if group.Type != "AFT" {
68+
return nil, fmt.Errorf("unknown test type %q", group.Scheme)
69+
}
70+
71+
switch group.Role {
72+
case "initiator", "responder":
73+
default:
74+
return nil, fmt.Errorf("unknown role %q", group.Role)
75+
}
76+
77+
if group.Scheme != "KTS-OAEP-basic" {
78+
return nil, fmt.Errorf("unknown scheme %q", group.Scheme)
79+
}
80+
81+
if len(group.KtsConfiguration.HashAlg) == 0 {
82+
return nil, fmt.Errorf("missing hash algorithm")
83+
}
84+
85+
hashAlg := group.KtsConfiguration.HashAlg
86+
87+
var outLenBytes [4]byte
88+
NativeEndian.PutUint32(outLenBytes[:], group.L/8) // Convert bits to bytes
89+
90+
for _, test := range group.Tests {
91+
if group.Role == "initiator" {
92+
result, err := m.Transact("KTS/OAEP/"+hashAlg+"/initiate", 2, outLenBytes[:], test.ServerN, test.ServerE)
93+
if err != nil {
94+
return nil, err
95+
}
96+
response.Tests = append(response.Tests, ktsTestResponse{
97+
ID: test.ID,
98+
IutC: result[0],
99+
Dkm: result[1],
100+
})
101+
} else {
102+
result, err := m.Transact("KTS/OAEP/"+hashAlg+"/respond", 1, test.Ct, test.IutN, test.IutE, test.IutQ, test.IutP, test.IutD)
103+
if err != nil {
104+
return nil, err
105+
}
106+
107+
response.Tests = append(response.Tests, ktsTestResponse{
108+
ID: test.ID,
109+
Dkm: result[0],
110+
})
111+
}
112+
}
113+
114+
ret = append(ret, response)
115+
}
116+
117+
return ret, nil
118+
}

util/fipstools/acvp/acvptool/subprocess/subprocess.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ func NewWithIO(cmd *exec.Cmd, in io.WriteCloser, out io.ReadCloser) *Subprocess
163163
"OneStep": &kdaOneStepMode{},
164164
},
165165
},
166+
"KTS-IFC": &kts{},
166167
"TLS-v1.3": &tls13{},
167168
"CMAC-AES": &keyedMACPrimitive{"CMAC-AES"},
168169
"RSA": &rsa{},

util/fipstools/acvp/acvptool/test/tests.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
{"Wrapper": "modulewrapper", "In": "vectors/KAS-ECC-SSC.bz2"},
2929
{"Wrapper": "modulewrapper", "In": "vectors/KAS-FFC-SSC.bz2"},
3030
{"Wrapper": "modulewrapper", "In": "vectors/KDF.bz2"},
31+
{"Wrapper": "modulewrapper", "In": "vectors/KTS-IFC.bz2"},
3132
{"Wrapper": "modulewrapper", "In": "vectors/ACVP-AES-GCM-internal-IV.bz2"},
3233
{"Wrapper": "modulewrapper", "In": "vectors/kdf-components.bz2", "Out": "expected/kdf-components.bz2"},
3334
{"Wrapper": "modulewrapper", "In": "vectors/RSA.bz2", "Out": "expected/RSA.bz2"},
35.4 KB
Binary file not shown.

util/fipstools/acvp/modulewrapper/modulewrapper.cc

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,6 +1554,25 @@ static bool GetConfig(const Span<const uint8_t> args[],
15541554
"internal",
15551555
"external"
15561556
]
1557+
},)"
1558+
R"({
1559+
"algorithm": "KTS-IFC",
1560+
"revision": "Sp800-56Br2",
1561+
"iutId": "ABCD",
1562+
"function": ["keyPairGen", "partialVal"],
1563+
"keyGenerationMethods": ["rsakpg1-basic"],
1564+
"modulo": [2048, 3072, 4096],
1565+
"fixedPubExp": "010001",
1566+
"scheme": {
1567+
"KTS-OAEP-basic": {
1568+
"kasRole": ["initiator", "responder"],
1569+
"ktsMethod": {
1570+
"hashAlgs": ["SHA-1", "SHA2-224", "SHA2-256", "SHA2-384", "SHA2-512"],
1571+
"supportsNullAssociatedData": true
1572+
},
1573+
"l": 1024
1574+
}
1575+
}
15571576
}])";
15581577
return write_reply({Span<const uint8_t>(
15591578
reinterpret_cast<const uint8_t *>(kConfig), sizeof(kConfig) - 1)});
@@ -2849,6 +2868,106 @@ static bool RSASigVer(const Span<const uint8_t> args[],
28492868
return write_reply({Span<const uint8_t>(&ok, 1)});
28502869
}
28512870

2871+
template <const EVP_MD *(MDFunc)()>
2872+
static bool RSAOAEPEncrypt(const Span<const uint8_t> args[],
2873+
ReplyCallback write_reply) {
2874+
const Span<const uint8_t> out_len_bytes = args[0];
2875+
const Span<const uint8_t> n_bytes = args[1];
2876+
const Span<const uint8_t> e_bytes = args[2];
2877+
2878+
uint32_t out_len = 0;
2879+
memcpy(&out_len, out_len_bytes.data(), sizeof(out_len));
2880+
2881+
BIGNUM *n = BN_new();
2882+
BIGNUM *e = BN_new();
2883+
bssl::UniquePtr<RSA> rsa(RSA_new());
2884+
2885+
if (!BN_bin2bn(n_bytes.data(), n_bytes.size(), n) ||
2886+
!BN_bin2bn(e_bytes.data(), e_bytes.size(), e) ||
2887+
!RSA_set0_key(rsa.get(), n, e, nullptr)) {
2888+
return false;
2889+
}
2890+
2891+
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
2892+
if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) {
2893+
return false;
2894+
}
2895+
2896+
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
2897+
if (!ctx || !EVP_PKEY_encrypt_init(ctx.get()) ||
2898+
!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) ||
2899+
!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), MDFunc())) {
2900+
return false;
2901+
}
2902+
2903+
// Randomly generate the keying material to encrypt
2904+
std::vector<uint8_t> out(out_len);
2905+
RAND_bytes(out.data(), out.size());
2906+
2907+
size_t ct_len = 0;
2908+
if (!EVP_PKEY_encrypt(ctx.get(), nullptr, &ct_len, out.data(), out.size())) {
2909+
return false;
2910+
}
2911+
std::vector<uint8_t> ct(ct_len);
2912+
if (!EVP_PKEY_encrypt(ctx.get(), ct.data(), &ct_len, out.data(),
2913+
out.size())) {
2914+
return false;
2915+
}
2916+
return write_reply({Span<const uint8_t>(ct), Span<const uint8_t>(out)});
2917+
}
2918+
2919+
template <const EVP_MD *(MDFunc)()>
2920+
static bool RSAOAEPDecrypt(const Span<const uint8_t> args[],
2921+
ReplyCallback write_reply) {
2922+
const Span<const uint8_t> input = args[0];
2923+
const Span<const uint8_t> n_bytes = args[1];
2924+
const Span<const uint8_t> e_bytes = args[2];
2925+
const Span<const uint8_t> q_bytes = args[3];
2926+
const Span<const uint8_t> p_bytes = args[4];
2927+
const Span<const uint8_t> d_bytes = args[5];
2928+
2929+
BIGNUM *n = BN_new();
2930+
BIGNUM *e = BN_new();
2931+
BIGNUM *p = BN_new();
2932+
BIGNUM *q = BN_new();
2933+
BIGNUM *d = BN_new();
2934+
bssl::UniquePtr<RSA> rsa(RSA_new());
2935+
2936+
if (!BN_bin2bn(n_bytes.data(), n_bytes.size(), n) ||
2937+
!BN_bin2bn(e_bytes.data(), e_bytes.size(), e) ||
2938+
!BN_bin2bn(d_bytes.data(), d_bytes.size(), d) ||
2939+
!BN_bin2bn(p_bytes.data(), p_bytes.size(), p) ||
2940+
!BN_bin2bn(q_bytes.data(), q_bytes.size(), q) ||
2941+
!RSA_set0_key(rsa.get(), n, e, d) || !RSA_set0_factors(rsa.get(), p, q)) {
2942+
return false;
2943+
}
2944+
2945+
bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
2946+
if (!EVP_PKEY_set1_RSA(pkey.get(), rsa.get())) {
2947+
return false;
2948+
}
2949+
2950+
bssl::UniquePtr<EVP_PKEY_CTX> ctx(EVP_PKEY_CTX_new(pkey.get(), nullptr));
2951+
if (!ctx || !EVP_PKEY_decrypt_init(ctx.get()) ||
2952+
!EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING) ||
2953+
!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), MDFunc())) {
2954+
return false;
2955+
}
2956+
2957+
size_t out_len = 0;
2958+
if (!EVP_PKEY_decrypt(ctx.get(), nullptr, &out_len, input.data(),
2959+
input.size())) {
2960+
return false;
2961+
}
2962+
std::vector<uint8_t> out(out_len);
2963+
if (!EVP_PKEY_decrypt(ctx.get(), out.data(), &out_len, input.data(),
2964+
input.size())) {
2965+
return false;
2966+
}
2967+
out.resize(out_len);
2968+
return write_reply({Span<const uint8_t>(out)});
2969+
}
2970+
28522971
template <const EVP_MD *(MDFunc)()>
28532972
static bool TLSKDF(const Span<const uint8_t> args[],
28542973
ReplyCallback write_reply) {
@@ -3881,6 +4000,16 @@ static struct {
38814000
{"KDA/OneStep/HMAC-SHA2-512", 4, SSKDF_HMAC<EVP_sha512>},
38824001
{"KDA/OneStep/HMAC-SHA2-512/224", 4, SSKDF_HMAC<EVP_sha512_224>},
38834002
{"KDA/OneStep/HMAC-SHA2-512/256", 4, SSKDF_HMAC<EVP_sha512_256>},
4003+
{"KTS/OAEP/SHA-1/initiate", 3, RSAOAEPEncrypt<EVP_sha1>},
4004+
{"KTS/OAEP/SHA2-224/initiate", 3, RSAOAEPEncrypt<EVP_sha224>},
4005+
{"KTS/OAEP/SHA2-256/initiate", 3, RSAOAEPEncrypt<EVP_sha256>},
4006+
{"KTS/OAEP/SHA2-384/initiate", 3, RSAOAEPEncrypt<EVP_sha384>},
4007+
{"KTS/OAEP/SHA2-512/initiate", 3, RSAOAEPEncrypt<EVP_sha512>},
4008+
{"KTS/OAEP/SHA-1/respond", 6, RSAOAEPDecrypt<EVP_sha1>},
4009+
{"KTS/OAEP/SHA2-224/respond", 6, RSAOAEPDecrypt<EVP_sha224>},
4010+
{"KTS/OAEP/SHA2-256/respond", 6, RSAOAEPDecrypt<EVP_sha256>},
4011+
{"KTS/OAEP/SHA2-384/respond", 6, RSAOAEPDecrypt<EVP_sha384>},
4012+
{"KTS/OAEP/SHA2-512/respond", 6, RSAOAEPDecrypt<EVP_sha512>},
38844013
{"SSHKDF/SHA-1/ivCli", 4,
38854014
SSHKDF<EVP_sha1, EVP_KDF_SSHKDF_TYPE_INITIAL_IV_CLI_TO_SRV>},
38864015
{"SSHKDF/SHA2-224/ivCli", 4,

0 commit comments

Comments
 (0)