Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
Signed-off-by: Vlad Gheorghiu <vsoftco@gmail.com>
  • Loading branch information
vsoftco committed Jan 15, 2025
1 parent e75e60e commit b8480fc
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 38 deletions.
11 changes: 11 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# Version 0.12.0 - January 15, 2025

- Fixes https://github.com/open-quantum-safe/liboqs-go/issues/44. The API that
NIST has introduced in [FIPS 204](https://csrc.nist.gov/pubs/fips/204/final)
for ML-DSA includes a context string of length >= 0. Added new API for
signing with a context string
- `func (sig *Signature)
SignWithCtxStr(message []byte, context []byte) ([]byte, error)`
- `func (sig *Signature)
VerifyWithCtxStr(message []byte, signature []byte, context []byte,
publicKey []byte) (bool, error)`
- Updated examples to use `ML-KEM` and `ML-DSA` as the defaults

# Version 0.10.0 - March 27, 2024

- Bumped Go version to 1.21
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,8 @@ The project contains the following files and directories:

Please note that on some platforms not all algorithms are supported:

- macOS/Darwin: The Rainbow and Classic-McEliece algorithm families as well as
HQC-256 do not work.
- Windows: The Rainbow and Classic-McEliece algorithm families do not work.
- macOS/Darwin: No known issues as of liboqs-0.12.0
- Windows: No known issues as of liboqs-0.12.0

---

Expand Down
173 changes: 143 additions & 30 deletions oqs/oqs.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,36 @@ func init() {

// KeyEncapsulationDetails defines the KEM algorithm details.
type KeyEncapsulationDetails struct {
Name string
Version string
ClaimedNISTLevel int
IsINDCCA bool
LengthCiphertext int
LengthPublicKey int
LengthSecretKey int
LengthCiphertext int
LengthSharedSecret int
Name string
Version string
}

// String converts the KEM algorithm details to a string representation. Use
// this method to pretty-print the KEM algorithm details, e.g.
// fmt.Println(client.Details()).
func (kemDetails KeyEncapsulationDetails) String() string {
return fmt.Sprintf("Name: %s\nVersion: %s\nClaimed NIST level: %d\n"+
"Is IND_CCA: %v\nLength public key (bytes): %d\nLength secret key ("+
"bytes): %d\nLength ciphertext (bytes): %d\nLength shared secret ("+
"bytes): %d", kemDetails.Name,
kemDetails.Version, kemDetails.ClaimedNISTLevel, kemDetails.IsINDCCA,
kemDetails.LengthPublicKey, kemDetails.LengthSecretKey,
kemDetails.LengthCiphertext, kemDetails.LengthSharedSecret)
return fmt.Sprintf("Name: %s\n"+
"Version: %s\n"+
"Claimed NIST level: %d\n"+
"Is IND_CCA: %v\n"+
"Length public key (bytes): %d\n"+
"Length secret key (bytes): %d\n"+
"Length ciphertext (bytes): %d\n"+
"Length shared secret (bytes): %d",
kemDetails.Name,
kemDetails.Version,
kemDetails.ClaimedNISTLevel,
kemDetails.IsINDCCA,
kemDetails.LengthPublicKey,
kemDetails.LengthSecretKey,
kemDetails.LengthCiphertext,
kemDetails.LengthSharedSecret)
}

// KeyEncapsulation defines the KEM main data structure.
Expand Down Expand Up @@ -170,9 +179,12 @@ func (kem *KeyEncapsulation) GenerateKeyPair() ([]byte, error) {
publicKey := make([]byte, kem.algDetails.LengthPublicKey)
kem.secretKey = make([]byte, kem.algDetails.LengthSecretKey)

rv := C.OQS_KEM_keypair(kem.kem,
rv := C.OQS_KEM_keypair(
kem.kem,
(*C.uint8_t)(unsafe.Pointer(&publicKey[0])),
(*C.uint8_t)(unsafe.Pointer(&kem.secretKey[0])))
(*C.uint8_t)(unsafe.Pointer(&kem.secretKey[0])),
)

if rv != C.OQS_SUCCESS {
return nil, errors.New("can not generate keypair")
}
Expand All @@ -197,10 +209,12 @@ func (kem *KeyEncapsulation) EncapSecret(publicKey []byte) (ciphertext,
ciphertext = make([]byte, kem.algDetails.LengthCiphertext)
sharedSecret = make([]byte, kem.algDetails.LengthSharedSecret)

rv := C.OQS_KEM_encaps(kem.kem,
rv := C.OQS_KEM_encaps(
kem.kem,
(*C.uint8_t)(unsafe.Pointer(&ciphertext[0])),
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
(*C.uint8_t)(unsafe.Pointer(&publicKey[0])))
(*C.uint8_t)(unsafe.Pointer(&publicKey[0])),
)

if rv != C.OQS_SUCCESS {
return nil, nil, errors.New("can not encapsulate secret")
Expand All @@ -222,10 +236,12 @@ func (kem *KeyEncapsulation) DecapSecret(ciphertext []byte) ([]byte, error) {
}

sharedSecret := make([]byte, kem.algDetails.LengthSharedSecret)
rv := C.OQS_KEM_decaps(kem.kem,
rv := C.OQS_KEM_decaps(
kem.kem,
(*C.uint8_t)(unsafe.Pointer(&sharedSecret[0])),
(*C.uchar)(unsafe.Pointer(&ciphertext[0])),
(*C.uint8_t)(unsafe.Pointer(&kem.secretKey[0])))
(*C.uint8_t)(unsafe.Pointer(&kem.secretKey[0])),
)

if rv != C.OQS_SUCCESS {
return nil, errors.New("can not decapsulate secret")
Expand Down Expand Up @@ -313,24 +329,35 @@ func init() {

// SignatureDetails defines the signature algorithm details.
type SignatureDetails struct {
Name string
Version string
ClaimedNISTLevel int
IsEUFCMA bool
SigWithCtxSupport bool
LengthPublicKey int
LengthSecretKey int
MaxLengthSignature int
Name string
Version string
}

// String converts the signature algorithm details to a string representation.
// Use this method to pretty-print the signature algorithm details, e.g.
// fmt.Println(signer.Details()).
func (sigDetails SignatureDetails) String() string {
return fmt.Sprintf("Name: %s\nVersion: %s\nClaimed NIST level: %d\n"+
"Is EUF_CMA: %v\nLength public key (bytes): %d\nLength secret key ("+
"bytes): %d\nMaximum length signature (bytes): %d", sigDetails.Name,
sigDetails.Version, sigDetails.ClaimedNISTLevel, sigDetails.IsEUFCMA,
sigDetails.LengthPublicKey, sigDetails.LengthSecretKey,
return fmt.Sprintf("Name: %s\n"+
"Version: %s\n"+
"Claimed NIST level: %d\n"+
"Is EUF_CMA: %v\n"+
"Supports context string: %v\n"+
"Length public key (bytes): %d\n"+
"Length secret key (bytes): %d\n"+
"Maximum length signature (bytes): %d",
sigDetails.Name,
sigDetails.Version,
sigDetails.ClaimedNISTLevel,
sigDetails.IsEUFCMA,
sigDetails.SigWithCtxSupport,
sigDetails.LengthPublicKey,
sigDetails.LengthSecretKey,
sigDetails.MaxLengthSignature)
}

Expand Down Expand Up @@ -370,9 +397,11 @@ func (sig *Signature) Init(algName string, secretKey []byte) error {
sig.algDetails.Version = C.GoString(sig.sig.alg_version)
sig.algDetails.ClaimedNISTLevel = int(sig.sig.claimed_nist_level)
sig.algDetails.IsEUFCMA = bool(sig.sig.euf_cma)
sig.algDetails.SigWithCtxSupport = bool(sig.sig.sig_with_ctx_support)
sig.algDetails.LengthPublicKey = int(sig.sig.length_public_key)
sig.algDetails.LengthSecretKey = int(sig.sig.length_secret_key)
sig.algDetails.MaxLengthSignature = int(sig.sig.length_signature)

return nil
}

Expand All @@ -389,9 +418,12 @@ func (sig *Signature) GenerateKeyPair() ([]byte, error) {
publicKey := make([]byte, sig.algDetails.LengthPublicKey)
sig.secretKey = make([]byte, sig.algDetails.LengthSecretKey)

rv := C.OQS_SIG_keypair(sig.sig,
rv := C.OQS_SIG_keypair(
sig.sig,
(*C.uint8_t)(unsafe.Pointer(&publicKey[0])),
(*C.uint8_t)(unsafe.Pointer(&sig.secretKey[0])))
(*C.uint8_t)(unsafe.Pointer(&sig.secretKey[0])),
)

if rv != C.OQS_SUCCESS {
return nil, errors.New("can not generate keypair")
}
Expand All @@ -413,10 +445,46 @@ func (sig *Signature) Sign(message []byte) ([]byte, error) {

signature := make([]byte, sig.algDetails.MaxLengthSignature)
var lenSig uint64
rv := C.OQS_SIG_sign(sig.sig, (*C.uint8_t)(unsafe.Pointer(&signature[0])),
rv := C.OQS_SIG_sign(
sig.sig,
(*C.uint8_t)(unsafe.Pointer(&signature[0])),
(*C.size_t)(unsafe.Pointer(&lenSig)),
(*C.uint8_t)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uint8_t)(unsafe.Pointer(&sig.secretKey[0])),
)

if rv != C.OQS_SUCCESS {
return nil, errors.New("can not sign message")
}

return signature[:lenSig], nil
}

// Sign signs a message with context string and returns the corresponding
// signature.
func (sig *Signature) SignWithCtxStr(message []byte, context []byte) ([]byte, error) {
if len(context) > 0 && !sig.algDetails.SigWithCtxSupport {
return nil, errors.New("can not sign message with context string")
}

if len(sig.secretKey) != sig.algDetails.LengthSecretKey {
return nil, errors.New("incorrect secret key length, make sure you " +
"specify one in Init() or run GenerateKeyPair()")
}

signature := make([]byte, sig.algDetails.MaxLengthSignature)
var lenSig uint64
rv := C.OQS_SIG_sign_with_ctx_str(
sig.sig,
(*C.uint8_t)(unsafe.Pointer(&signature[0])),
(*C.size_t)(unsafe.Pointer(&lenSig)),
(*C.uint8_t)(unsafe.Pointer(&message[0])),
C.size_t(len(message)), (*C.uint8_t)(unsafe.Pointer(&sig.secretKey[0])))
C.size_t(len(message)),
(*C.uint8_t)(unsafe.Pointer(&context[0])),
C.size_t(len(context)),
(*C.uint8_t)(unsafe.Pointer(&sig.secretKey[0])),
)

if rv != C.OQS_SUCCESS {
return nil, errors.New("can not sign message")
Expand All @@ -438,9 +506,54 @@ func (sig *Signature) Verify(message []byte, signature []byte,
return false, errors.New("incorrect signature size")
}

rv := C.OQS_SIG_verify(sig.sig, (*C.uint8_t)(unsafe.Pointer(&message[0])),
C.size_t(len(message)), (*C.uint8_t)(unsafe.Pointer(&signature[0])),
C.size_t(len(signature)), (*C.uint8_t)(unsafe.Pointer(&publicKey[0])))
rv := C.OQS_SIG_verify(
sig.sig,
(*C.uint8_t)(unsafe.Pointer(&message[0])),
C.size_t(len(message)),
(*C.uint8_t)(unsafe.Pointer(&signature[0])),
C.size_t(len(signature)),
(*C.uint8_t)(unsafe.Pointer(&publicKey[0])),
)

if rv != C.OQS_SUCCESS {
return false, nil
}

return true, nil
}

// Verify verifies the validity of a signed message with context string,
// returning true if the signature is valid, and false otherwise.
func (sig *Signature) VerifyWithCtxStr(
message []byte,
signature []byte,
context []byte,
publicKey []byte,
) (bool, error) {
if len(context) > 0 && !sig.algDetails.SigWithCtxSupport {
return false, errors.New("can not sign message with context string")
}

if len(publicKey) != sig.algDetails.LengthPublicKey {
return false, errors.New("incorrect public key length")
}

if len(signature) > sig.algDetails.MaxLengthSignature {
return false, errors.New("incorrect signature size")
}

rv := C.OQS_SIG_verify_with_ctx_str(
sig.sig,
(*C.uint8_t)(
unsafe.Pointer(&message[0]),
),
C.size_t(len(message)),
(*C.uint8_t)(unsafe.Pointer(&signature[0])),
C.size_t(len(signature)),
(*C.uint8_t)(unsafe.Pointer(&context[0])),
C.size_t(len(context)),
(*C.uint8_t)(unsafe.Pointer(&publicKey[0])),
)

if rv != C.OQS_SUCCESS {
return false, nil
Expand Down
4 changes: 2 additions & 2 deletions oqstests/kem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func testKEMCorrectness(kemName string, threading bool, t *testing.T) {
sharedSecretClient, _ := client.DecapSecret(ciphertext)
if !bytes.Equal(sharedSecretClient, sharedSecretServer) {
// t.Errorf is thread-safe
t.Errorf(kemName + ": shared secrets do not coincide")
t.Errorf("%s: shared secrets do not coincide", kemName)
}
}

Expand All @@ -63,7 +63,7 @@ func testKEMWrongCiphertext(kemName string, threading bool, t *testing.T) {
sharedSecretClient, _ := client.DecapSecret(wrongCiphertext)
if bytes.Equal(sharedSecretClient, sharedSecretServer) {
// t.Errorf is thread-safe
t.Errorf(kemName + ": shared secrets should not coincide")
t.Errorf("%s: shared secrets should not coincide", kemName)
}
}

Expand Down
27 changes: 24 additions & 3 deletions oqstests/sig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,28 @@ func testSigCorrectness(sigName string, msg []byte, threading bool, t *testing.T
isValid, _ := verifier.Verify(msg, signature, pubKey)
if !isValid {
// t.Errorf is thread-safe
t.Errorf(sigName + ": signature verification failed")
t.Errorf("%s: signature verification failed", sigName)
}
}

// testSigCorrectness tests a specific signature with context string.
func testSigCorrectnessWithCtxStr(sigName string, msg []byte, threading bool, t *testing.T) {
log.Println("Correctness - ", sigName) // thread-safe
if threading == true {
defer wgSigCorrectness.Done()
}
var signer, verifier oqs.Signature
defer signer.Clean()
defer verifier.Clean()
// Ignore potential errors everywhere
_ = signer.Init(sigName, nil)
_ = verifier.Init(sigName, nil)
pubKey, _ := signer.GenerateKeyPair()
signature, _ := signer.Sign(msg)
isValid, _ := verifier.Verify(msg, signature, pubKey)
if !isValid {
// t.Errorf is thread-safe
t.Errorf("%s: signature verification failed", sigName)
}
}

Expand All @@ -64,7 +85,7 @@ func testSigWrongSignature(sigName string, msg []byte, threading bool, t *testin
isValid, _ := verifier.Verify(msg, wrongSignature, pubKey)
if isValid {
// t.Errorf is thread-safe
t.Errorf(sigName + ": signature verification should have failed")
t.Errorf("%s: signature verification should have failed", sigName)
}
}

Expand All @@ -86,7 +107,7 @@ func testSigWrongPublicKey(sigName string, msg []byte, threading bool, t *testin
isValid, _ := verifier.Verify(msg, signature, wrongPubKey)
if isValid {
// t.Errorf is thread-safe
t.Errorf(sigName + ": signature verification should have failed")
t.Errorf("%s: signature verification should have failed", sigName)
}
}

Expand Down

0 comments on commit b8480fc

Please sign in to comment.