Skip to content

Commit ba6c14e

Browse files
committed
mlkem: export the seed instead of the raw private key
1 parent 63d23e5 commit ba6c14e

File tree

3 files changed

+35
-41
lines changed

3 files changed

+35
-41
lines changed

cng/mlkem.go

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,11 @@ const (
2626
// encapsulationKeySizeMLKEM768 is the size of an ML-KEM-768 encapsulation key (raw key material).
2727
encapsulationKeySizeMLKEM768 = 1184
2828

29-
// decapsulationKeySizeMLKEM768 is the size of the ML-KEM-768 decapsulation key data (raw key material).
30-
decapsulationKeySizeMLKEM768 = 2400
31-
3229
// ciphertextSizeMLKEM1024 is the size of a ciphertext produced by ML-KEM-1024.
3330
ciphertextSizeMLKEM1024 = 1568
3431

3532
// encapsulationKeySizeMLKEM1024 is the size of an ML-KEM-1024 encapsulation key (raw key material).
3633
encapsulationKeySizeMLKEM1024 = 1568
37-
38-
// decapsulationKeySizeMLKEM1024 is the size of the ML-KEM-1024 decapsulation key data (raw key material).
39-
decapsulationKeySizeMLKEM1024 = 3168
4034
)
4135

4236
// putUint32LE puts a uint32 in little-endian byte order.
@@ -102,13 +96,13 @@ func generateMLKEMKey(paramSet string, dst []byte) error {
10296

10397
// Export the private key blob
10498
var size uint32
105-
err = bcrypt.ExportKey(hKey, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_BLOB), nil, &size, 0)
99+
err = bcrypt.ExportKey(hKey, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_SEED_BLOB), nil, &size, 0)
106100
if err != nil {
107101
return err
108102
}
109103

110104
blob := make([]byte, size)
111-
err = bcrypt.ExportKey(hKey, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_BLOB), blob, &size, 0)
105+
err = bcrypt.ExportKey(hKey, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_SEED_BLOB), blob, &size, 0)
112106
if err != nil {
113107
return err
114108
}
@@ -153,7 +147,7 @@ func extractMLKEMKeyBytes(blob []byte, dst []byte) error {
153147
}
154148

155149
// mlkemDecapsulate is a shared helper for decapsulating with ML-KEM keys.
156-
func mlkemDecapsulate(paramSet string, keyBytes []byte, ciphertext []byte, expectedCiphertextSize int) ([]byte, error) {
150+
func mlkemDecapsulate(paramSet string, seed []byte, ciphertext []byte, expectedCiphertextSize int) ([]byte, error) {
157151
if len(ciphertext) != expectedCiphertextSize {
158152
return nil, errors.New("mlkem: invalid ciphertext size")
159153
}
@@ -164,13 +158,13 @@ func mlkemDecapsulate(paramSet string, keyBytes []byte, ciphertext []byte, expec
164158
}
165159

166160
// Construct blob from raw key bytes
167-
blob, err := newMLKEMKeyBlob(paramSet, keyBytes, bcrypt.MLKEM_PRIVATE_MAGIC)
161+
blob, err := newMLKEMKeyBlob(paramSet, seed, bcrypt.MLKEM_PRIVATE_SEED_MAGIC)
168162
if err != nil {
169163
return nil, err
170164
}
171165

172166
var hKey bcrypt.KEY_HANDLE
173-
err = bcrypt.ImportKeyPair(alg.handle, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_BLOB), &hKey, blob, 0)
167+
err = bcrypt.ImportKeyPair(alg.handle, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_SEED_BLOB), &hKey, blob, 0)
174168
if err != nil {
175169
return nil, err
176170
}
@@ -187,20 +181,20 @@ func mlkemDecapsulate(paramSet string, keyBytes []byte, ciphertext []byte, expec
187181
}
188182

189183
// mlkemEncapsulationKey is a shared helper for extracting the encapsulation key from a decapsulation key.
190-
func mlkemEncapsulationKey(paramSet string, keyBytes []byte, dst []byte) error {
184+
func mlkemEncapsulationKey(paramSet string, seed []byte, dst []byte) error {
191185
alg, err := loadMLKEM()
192186
if err != nil {
193187
return err
194188
}
195189

196190
// Construct blob from raw key bytes
197-
blob, err := newMLKEMKeyBlob(paramSet, keyBytes, bcrypt.MLKEM_PRIVATE_MAGIC)
191+
blob, err := newMLKEMKeyBlob(paramSet, seed, bcrypt.MLKEM_PRIVATE_SEED_MAGIC)
198192
if err != nil {
199193
return err
200194
}
201195

202196
var hKey bcrypt.KEY_HANDLE
203-
err = bcrypt.ImportKeyPair(alg.handle, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_BLOB), &hKey, blob, 0)
197+
err = bcrypt.ImportKeyPair(alg.handle, 0, utf16PtrFromString(bcrypt.MLKEM_PRIVATE_SEED_BLOB), &hKey, blob, 0)
204198
if err != nil {
205199
return err
206200
}
@@ -258,9 +252,7 @@ func mlkemEncapsulate(paramSet string, keyBytes []byte, expectedCiphertextSize i
258252

259253
// DecapsulationKeyMLKEM768 is the secret key used to decapsulate a shared key
260254
// from a ciphertext. It includes various precomputed values.
261-
// Note: Unlike the stdlib crypto/mlkem implementation which uses a 64-byte seed,
262-
// the Windows CNG implementation stores the full 2400-byte expanded key material.
263-
type DecapsulationKeyMLKEM768 [decapsulationKeySizeMLKEM768]byte
255+
type DecapsulationKeyMLKEM768 [seedSizeMLKEM]byte
264256

265257
// GenerateKeyMLKEM768 generates a new decapsulation key, drawing random bytes from
266258
// the default crypto/rand source. The decapsulation key must be kept secret.
@@ -273,21 +265,17 @@ func GenerateKeyMLKEM768() (DecapsulationKeyMLKEM768, error) {
273265
}
274266

275267
// NewDecapsulationKeyMLKEM768 constructs a decapsulation key from its serialized form.
276-
// Note: Unlike the stdlib crypto/mlkem which expects a 64-byte seed, this function
277-
// expects the full 2400-byte expanded key material as returned by Bytes().
278-
func NewDecapsulationKeyMLKEM768(keyBytes []byte) (DecapsulationKeyMLKEM768, error) {
279-
if len(keyBytes) != decapsulationKeySizeMLKEM768 {
268+
func NewDecapsulationKeyMLKEM768(seed []byte) (DecapsulationKeyMLKEM768, error) {
269+
if len(seed) != seedSizeMLKEM {
280270
return DecapsulationKeyMLKEM768{}, errors.New("mlkem: invalid decapsulation key size")
281271
}
282272

283273
var dk DecapsulationKeyMLKEM768
284-
copy(dk[:], keyBytes)
274+
copy(dk[:], seed)
285275
return dk, nil
286276
}
287277

288278
// Bytes returns the decapsulation key in its serialized form.
289-
// Note: Unlike the stdlib crypto/mlkem which returns a 64-byte seed, this returns
290-
// the full 2400-byte expanded key material.
291279
//
292280
// The decapsulation key must be kept secret.
293281
func (dk DecapsulationKeyMLKEM768) Bytes() []byte {
@@ -352,9 +340,7 @@ func (ek EncapsulationKeyMLKEM768) Encapsulate() (sharedKey, ciphertext []byte)
352340

353341
// DecapsulationKeyMLKEM1024 is the secret key used to decapsulate a shared key
354342
// from a ciphertext. It includes various precomputed values.
355-
// Note: Unlike the stdlib crypto/mlkem implementation which uses a 64-byte seed,
356-
// the Windows CNG implementation stores the full 3168-byte expanded key material.
357-
type DecapsulationKeyMLKEM1024 [decapsulationKeySizeMLKEM1024]byte
343+
type DecapsulationKeyMLKEM1024 [seedSizeMLKEM]byte
358344

359345
// GenerateKeyMLKEM1024 generates a new decapsulation key, drawing random bytes from
360346
// the default crypto/rand source. The decapsulation key must be kept secret.
@@ -369,13 +355,13 @@ func GenerateKeyMLKEM1024() (DecapsulationKeyMLKEM1024, error) {
369355
// NewDecapsulationKeyMLKEM1024 constructs a decapsulation key from its serialized form.
370356
// Note: Unlike the stdlib crypto/mlkem which expects a 64-byte seed, this function
371357
// expects the full 3168-byte expanded key material as returned by Bytes().
372-
func NewDecapsulationKeyMLKEM1024(keyBytes []byte) (DecapsulationKeyMLKEM1024, error) {
373-
if len(keyBytes) != decapsulationKeySizeMLKEM1024 {
358+
func NewDecapsulationKeyMLKEM1024(seed []byte) (DecapsulationKeyMLKEM1024, error) {
359+
if len(seed) != seedSizeMLKEM {
374360
return DecapsulationKeyMLKEM1024{}, errors.New("mlkem: invalid decapsulation key size")
375361
}
376362

377363
var dk DecapsulationKeyMLKEM1024
378-
copy(dk[:], keyBytes)
364+
copy(dk[:], seed)
379365
return dk, nil
380366
}
381367

cng/mlkem_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ package cng_test
99
import (
1010
"bytes"
1111
"crypto/mlkem"
12-
"math/rand"
12+
"crypto/rand"
1313
"testing"
1414

1515
"github.com/microsoft/go-crypto-winnative/cng"
@@ -369,6 +369,10 @@ func TestMLKEMInteropWithStdlib(t *testing.T) {
369369
t.Fatal(err)
370370
}
371371

372+
if len(cngDK.Bytes()) != len(stdlibDK.Bytes()) {
373+
t.Fatalf("decapsulation key sizes don't match: CNG=%d, stdlib=%d", len(cngDK.Bytes()), len(stdlibDK.Bytes()))
374+
}
375+
372376
// Test CNG encapsulation key -> stdlib
373377
cngEKBytes := cngDK.EncapsulationKey().Bytes()
374378
stdlibEK, err := mlkem.NewEncapsulationKey768(cngEKBytes)
@@ -477,6 +481,9 @@ func TestMLKEMInteropWithStdlib(t *testing.T) {
477481
if err != nil {
478482
t.Fatal(err)
479483
}
484+
if len(cngDK.Bytes()) != len(stdlibDK.Bytes()) {
485+
t.Fatalf("decapsulation key sizes don't match: CNG=%d, stdlib=%d", len(cngDK.Bytes()), len(stdlibDK.Bytes()))
486+
}
480487

481488
// Test CNG encapsulation key -> stdlib
482489
cngEKBytes := cngDK.EncapsulationKey().Bytes()

internal/bcrypt/bcrypt_windows.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,14 @@ const (
6666
)
6767

6868
const (
69-
RSAPUBLIC_KEY_BLOB = "RSAPUBLICBLOB"
70-
RSAFULLPRIVATE_BLOB = "RSAFULLPRIVATEBLOB"
71-
ECCPUBLIC_BLOB = "ECCPUBLICBLOB"
72-
ECCPRIVATE_BLOB = "ECCPRIVATEBLOB"
73-
DSA_PUBLIC_BLOB = "DSAPUBLICBLOB"
74-
DSA_PRIVATE_BLOB = "DSAPRIVATEBLOB"
75-
MLKEM_PUBLIC_BLOB = "MLKEMPUBLICBLOB"
76-
MLKEM_PRIVATE_BLOB = "MLKEMPRIVATEBLOB"
69+
RSAPUBLIC_KEY_BLOB = "RSAPUBLICBLOB"
70+
RSAFULLPRIVATE_BLOB = "RSAFULLPRIVATEBLOB"
71+
ECCPUBLIC_BLOB = "ECCPUBLICBLOB"
72+
ECCPRIVATE_BLOB = "ECCPRIVATEBLOB"
73+
DSA_PUBLIC_BLOB = "DSAPUBLICBLOB"
74+
DSA_PRIVATE_BLOB = "DSAPRIVATEBLOB"
75+
MLKEM_PUBLIC_BLOB = "MLKEMPUBLICBLOB"
76+
MLKEM_PRIVATE_SEED_BLOB = "MLKEMPRIVATESEEDBLOB"
7777
)
7878

7979
const (
@@ -212,8 +212,9 @@ const (
212212
DSA_PUBLIC_MAGIC_V2 KeyBlobMagicNumber = 0x32425044
213213
DSA_PRIVATE_MAGIC_V2 KeyBlobMagicNumber = 0x32565044
214214

215-
MLKEM_PUBLIC_MAGIC KeyBlobMagicNumber = 0x504b4c4d
216-
MLKEM_PRIVATE_MAGIC KeyBlobMagicNumber = 0x524b4c4d
215+
MLKEM_PUBLIC_MAGIC KeyBlobMagicNumber = 0x504B4C4D
216+
MLKEM_PRIVATE_MAGIC KeyBlobMagicNumber = 0x524B4C4D
217+
MLKEM_PRIVATE_SEED_MAGIC KeyBlobMagicNumber = 0x534B4C4D
217218
)
218219

219220
type (

0 commit comments

Comments
 (0)