@@ -26,6 +26,7 @@ import (
26
26
"crypto/internal/fips140"
27
27
"crypto/internal/fips140/aes"
28
28
"crypto/internal/fips140/aes/gcm"
29
+ "crypto/internal/fips140/drbg"
29
30
"crypto/internal/fips140/ecdh"
30
31
"crypto/internal/fips140/ecdsa"
31
32
"crypto/internal/fips140/ed25519"
@@ -125,7 +126,9 @@ var (
125
126
// SSH KDF algorithm capabilities:
126
127
// https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-ssh.html#section-7.2
127
128
// ECDH algorithm capabilities:
128
- // https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html
129
+ // https://pages.nist.gov/ACVP/draft-hammett-acvp-kas-ssc-ecc.html#section-7.3
130
+ // HMAC DRBG and CTR DRBG algorithm capabilities:
131
+ // https://pages.nist.gov/ACVP/draft-vassilev-acvp-drbg.html#section-7.2
129
132
//go:embed acvp_capabilities.json
130
133
capabilitiesJson []byte
131
134
@@ -259,6 +262,9 @@ var (
259
262
"ECDH/P-256" : cmdEcdhAftVal (ecdh .P256 ()),
260
263
"ECDH/P-384" : cmdEcdhAftVal (ecdh .P384 ()),
261
264
"ECDH/P-521" : cmdEcdhAftVal (ecdh .P521 ()),
265
+
266
+ "ctrDRBG/AES-256" : cmdCtrDrbgAft (),
267
+ "ctrDRBG-reseed/AES-256" : cmdCtrDrbgReseedAft (),
262
268
}
263
269
)
264
270
@@ -1103,39 +1109,6 @@ func cmdMlKem1024DecapAft() command {
1103
1109
}
1104
1110
}
1105
1111
1106
- func cmdHmacDrbgAft (h func () fips140.Hash ) command {
1107
- return command {
1108
- requiredArgs : 6 , // Output length, entropy, personalization, ad1, ad2, nonce
1109
- handler : func (args [][]byte ) ([][]byte , error ) {
1110
- outLen := binary .LittleEndian .Uint32 (args [0 ])
1111
- entropy := args [1 ]
1112
- personalization := args [2 ]
1113
- ad1 := args [3 ]
1114
- ad2 := args [4 ]
1115
- nonce := args [5 ]
1116
-
1117
- // Our capabilities describe no additional data support.
1118
- if len (ad1 ) != 0 || len (ad2 ) != 0 {
1119
- return nil , errors .New ("additional data not supported" )
1120
- }
1121
-
1122
- // Our capabilities describe no prediction resistance (requires reseed) and no reseed.
1123
- // So the test procedure is:
1124
- // * Instantiate DRBG
1125
- // * Generate but don't output
1126
- // * Generate output
1127
- // * Uninstantiate
1128
- // See Table 7 in draft-vassilev-acvp-drbg
1129
- out := make ([]byte , outLen )
1130
- drbg := ecdsa .TestingOnlyNewDRBG (h , entropy , nonce , personalization )
1131
- drbg .Generate (out )
1132
- drbg .Generate (out )
1133
-
1134
- return [][]byte {out }, nil
1135
- },
1136
- }
1137
- }
1138
-
1139
1112
func lookupCurve (name string ) (elliptic.Curve , error ) {
1140
1113
var c elliptic.Curve
1141
1114
@@ -1460,6 +1433,152 @@ func cmdEcdhAftVal[P ecdh.Point[P]](curve *ecdh.Curve[P]) command {
1460
1433
}
1461
1434
}
1462
1435
1436
+ func cmdHmacDrbgAft (h func () fips140.Hash ) command {
1437
+ return command {
1438
+ requiredArgs : 6 , // Output length, entropy, personalization, ad1, ad2, nonce
1439
+ handler : func (args [][]byte ) ([][]byte , error ) {
1440
+ outLen := binary .LittleEndian .Uint32 (args [0 ])
1441
+ entropy := args [1 ]
1442
+ personalization := args [2 ]
1443
+ ad1 := args [3 ]
1444
+ ad2 := args [4 ]
1445
+ nonce := args [5 ]
1446
+
1447
+ // Our capabilities describe no additional data support.
1448
+ if len (ad1 ) != 0 || len (ad2 ) != 0 {
1449
+ return nil , errors .New ("additional data not supported" )
1450
+ }
1451
+
1452
+ // Our capabilities describe no prediction resistance (requires reseed) and no reseed.
1453
+ // So the test procedure is:
1454
+ // * Instantiate DRBG
1455
+ // * Generate but don't output
1456
+ // * Generate output
1457
+ // * Uninstantiate
1458
+ // See Table 7 in draft-vassilev-acvp-drbg
1459
+ out := make ([]byte , outLen )
1460
+ drbg := ecdsa .TestingOnlyNewDRBG (h , entropy , nonce , personalization )
1461
+ drbg .Generate (out )
1462
+ drbg .Generate (out )
1463
+
1464
+ return [][]byte {out }, nil
1465
+ },
1466
+ }
1467
+ }
1468
+
1469
+ func cmdCtrDrbgAft () command {
1470
+ return command {
1471
+ requiredArgs : 6 , // Output length, entropy, personalization, ad1, ad2, nonce
1472
+ handler : func (args [][]byte ) ([][]byte , error ) {
1473
+ return acvpCtrDrbg {
1474
+ outLen : binary .LittleEndian .Uint32 (args [0 ]),
1475
+ entropy : args [1 ],
1476
+ personalization : args [2 ],
1477
+ ad1 : args [3 ],
1478
+ ad2 : args [4 ],
1479
+ nonce : args [5 ],
1480
+ }.process ()
1481
+ },
1482
+ }
1483
+ }
1484
+
1485
+ func cmdCtrDrbgReseedAft () command {
1486
+ return command {
1487
+ requiredArgs : 8 , // Output length, entropy, personalization, reseedAD, reseedEntropy, ad1, ad2, nonce
1488
+ handler : func (args [][]byte ) ([][]byte , error ) {
1489
+ return acvpCtrDrbg {
1490
+ outLen : binary .LittleEndian .Uint32 (args [0 ]),
1491
+ entropy : args [1 ],
1492
+ personalization : args [2 ],
1493
+ reseedAd : args [3 ],
1494
+ reseedEntropy : args [4 ],
1495
+ ad1 : args [5 ],
1496
+ ad2 : args [6 ],
1497
+ nonce : args [7 ],
1498
+ }.process ()
1499
+ },
1500
+ }
1501
+ }
1502
+
1503
+ type acvpCtrDrbg struct {
1504
+ outLen uint32
1505
+ entropy []byte
1506
+ personalization []byte
1507
+ ad1 []byte
1508
+ ad2 []byte
1509
+ nonce []byte
1510
+ reseedAd []byte // May be empty for no reseed
1511
+ reseedEntropy []byte // May be empty for no reseed
1512
+ }
1513
+
1514
+ func (args acvpCtrDrbg ) process () ([][]byte , error ) {
1515
+ // Our capability describes no personalization support.
1516
+ if len (args .personalization ) > 0 {
1517
+ return nil , errors .New ("personalization string not supported" )
1518
+ }
1519
+
1520
+ // Our capability describes no derivation function support, so the nonce
1521
+ // should be empty.
1522
+ if len (args .nonce ) > 0 {
1523
+ return nil , errors .New ("unexpected nonce value" )
1524
+ }
1525
+
1526
+ // Our capability describes entropy input len of 384 bits.
1527
+ entropy , err := require48Bytes (args .entropy )
1528
+ if err != nil {
1529
+ return nil , fmt .Errorf ("entropy: %w" , err )
1530
+ }
1531
+
1532
+ // Our capability describes additional input len of 384 bits.
1533
+ ad1 , err := require48Bytes (args .ad1 )
1534
+ if err != nil {
1535
+ return nil , fmt .Errorf ("AD1: %w" , err )
1536
+ }
1537
+ ad2 , err := require48Bytes (args .ad2 )
1538
+ if err != nil {
1539
+ return nil , fmt .Errorf ("AD2: %w" , err )
1540
+ }
1541
+
1542
+ withReseed := len (args .reseedAd ) > 0
1543
+ var reseedAd , reseedEntropy * [48 ]byte
1544
+ if withReseed {
1545
+ // Ditto RE: entropy and additional data lengths for reseeding.
1546
+ if reseedAd , err = require48Bytes (args .reseedAd ); err != nil {
1547
+ return nil , fmt .Errorf ("reseed AD: %w" , err )
1548
+ }
1549
+ if reseedEntropy , err = require48Bytes (args .reseedEntropy ); err != nil {
1550
+ return nil , fmt .Errorf ("reseed entropy: %w" , err )
1551
+ }
1552
+ }
1553
+
1554
+ // Our capabilities describe no prediction resistance and allow both
1555
+ // reseed and no reseed, so the test procedure is:
1556
+ // * Instantiate DRBG
1557
+ // * Reseed (if enabled)
1558
+ // * Generate but don't output
1559
+ // * Generate output
1560
+ // * Uninstantiate
1561
+ // See Table 7 in draft-vassilev-acvp-drbg
1562
+ out := make ([]byte , args .outLen )
1563
+ ctrDrbg := drbg .NewCounter (entropy )
1564
+ if withReseed {
1565
+ ctrDrbg .Reseed (reseedEntropy , reseedAd )
1566
+ }
1567
+ ctrDrbg .Generate (out , ad1 )
1568
+ ctrDrbg .Generate (out , ad2 )
1569
+
1570
+ return [][]byte {out }, nil
1571
+ }
1572
+
1573
+ // Verify input is 48 byte slice, and cast it to a pointer to a fixed-size array
1574
+ // of 48 bytes, or return an error.
1575
+ func require48Bytes (input []byte ) (* [48 ]byte , error ) {
1576
+ if inputLen := len (input ); inputLen != 48 {
1577
+ return nil , fmt .Errorf ("invalid length: %d" , inputLen )
1578
+ }
1579
+ return (* [48 ]byte )(input ), nil
1580
+ }
1581
+
1463
1582
func TestACVP (t * testing.T ) {
1464
1583
testenv .SkipIfShortAndSlow (t )
1465
1584
0 commit comments