From b96901b055a7b79ff6590a18f7fd85fe8a06167b Mon Sep 17 00:00:00 2001 From: oleiade Date: Fri, 14 Apr 2023 11:48:16 +0200 Subject: [PATCH] Add `bitLength` and `byteLength` types for bytes/bits length conversions --- webcrypto/aes.go | 22 +++++++++++----------- webcrypto/hmac.go | 31 +++++++++++++------------------ webcrypto/types.go | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/webcrypto/aes.go b/webcrypto/aes.go index 5dac06c..63277b1 100644 --- a/webcrypto/aes.go +++ b/webcrypto/aes.go @@ -20,7 +20,7 @@ type AESKeyGenParams struct { Algorithm // The length, in bits, of the key. - Length int64 `json:"length"` + Length bitLength `json:"length"` } // newAESKeyGenParams creates a new AESKeyGenParams object, from the @@ -41,7 +41,7 @@ func newAESKeyGenParams(rt *goja.Runtime, normalized Algorithm, params goja.Valu return &AESKeyGenParams{ Algorithm: normalized, - Length: algorithmLength, + Length: bitLength(algorithmLength), }, nil } @@ -75,7 +75,7 @@ func (akgp *AESKeyGenParams) GenerateKey( return nil, NewError(OperationError, "invalid key length") } - randomKey := make([]byte, akgp.Length/8) + randomKey := make([]byte, akgp.Length.asByteLength()) if _, err := rand.Read(randomKey); err != nil { // 4. return nil, NewError(OperationError, "could not generate random key") @@ -86,7 +86,7 @@ func (akgp *AESKeyGenParams) GenerateKey( key.Type = SecretCryptoKeyType key.Algorithm = AESKeyAlgorithm{ Algorithm: akgp.Algorithm, - Length: akgp.Length, + Length: int64(akgp.Length), } // 10. @@ -190,7 +190,7 @@ func (aip *AESImportParams) ImportKey( key := &CryptoKey{ Algorithm: AESKeyAlgorithm{ Algorithm: aip.Algorithm, - Length: int64(len(keyData) * 8), + Length: int64(byteLength(len(keyData)).asBitLength()), }, Type: SecretCryptoKeyType, handle: keyData, @@ -443,7 +443,7 @@ type AESGCMParams struct { // in some applications: Appendix C of the specification provides additional guidance here. // // tagLength is optional and defaults to 128 if it is not specified. - TagLength int `json:"tagLength"` + TagLength bitLength `json:"tagLength"` } // Encrypt encrypts the given plaintext using the AES-GCM algorithm, and returns the ciphertext. @@ -477,7 +477,7 @@ func (agp *AESGCMParams) Encrypt(plaintext []byte, key CryptoKey) ([]byte, error } // 4. - var tagLength int + var tagLength bitLength if agp.TagLength == 0 { tagLength = 128 } else { @@ -503,7 +503,7 @@ func (agp *AESGCMParams) Encrypt(plaintext []byte, key CryptoKey) ([]byte, error return nil, NewError(OperationError, "could not create cipher") } - gcm, err := cipher.NewGCMWithTagSize(block, tagLength/8) + gcm, err := cipher.NewGCMWithTagSize(block, int(tagLength.asByteLength())) if err != nil { return nil, NewError(ImplementationError, "could not create cipher") } @@ -526,7 +526,7 @@ func (agp *AESGCMParams) Encrypt(plaintext []byte, key CryptoKey) ([]byte, error // [specification]: https://www.w3.org/TR/WebCryptoAPI/#aes-gcm func (agp *AESGCMParams) Decrypt(ciphertext []byte, key CryptoKey) ([]byte, error) { // 1. - var tagLength int + var tagLength bitLength if agp.TagLength == 0 { tagLength = 128 } else { @@ -544,7 +544,7 @@ func (agp *AESGCMParams) Decrypt(ciphertext []byte, key CryptoKey) ([]byte, erro // 2. // Note that we multiply the length of the ciphertext by 8, in order // to get the length in bits. - if len(ciphertext)*8 < tagLength { + if byteLength(len(ciphertext)).asBitLength() < tagLength { return nil, NewError(OperationError, "ciphertext is too short") } @@ -571,7 +571,7 @@ func (agp *AESGCMParams) Decrypt(ciphertext []byte, key CryptoKey) ([]byte, erro return nil, NewError(OperationError, "could not create AES cipher") } - gcm, err := cipher.NewGCMWithTagSize(block, tagLength/8) + gcm, err := cipher.NewGCMWithTagSize(block, int(tagLength.asByteLength())) if err != nil { return nil, NewError(OperationError, "could not create GCM cipher") } diff --git a/webcrypto/hmac.go b/webcrypto/hmac.go index cb2322c..1c93ad8 100644 --- a/webcrypto/hmac.go +++ b/webcrypto/hmac.go @@ -98,20 +98,23 @@ func (hkgp *HMACKeyGenParams) GenerateKey( // part of the normalized algorithm, and as accessing the runtime from the // callback below could lead to a race condition. if !hkgp.Length.Valid { + var length bitLength switch hkgp.Hash.Name { case SHA1: - hkgp.Length = null.IntFrom(sha1.BlockSize * 8) + length = byteLength(sha1.BlockSize).asBitLength() case SHA256: - hkgp.Length = null.IntFrom(sha256.BlockSize * 8) + length = byteLength(sha256.BlockSize).asBitLength() case SHA384: - hkgp.Length = null.IntFrom(sha512.BlockSize * 8) + length = byteLength(sha512.BlockSize).asBitLength() case SHA512: - hkgp.Length = null.IntFrom(sha512.BlockSize * 8) + length = byteLength(sha512.BlockSize).asBitLength() default: // This case should never happen, as the normalization algorithm // should have thrown an error if the hash algorithm is invalid. return nil, NewError(ImplementationError, "invalid hash algorithm: "+hkgp.Hash.Name) } + + hkgp.Length = null.IntFrom(int64(length)) } if hkgp.Length.Int64 == 0 { @@ -119,7 +122,7 @@ func (hkgp *HMACKeyGenParams) GenerateKey( } // 3. - randomKey := make([]byte, hkgp.Length.Int64/8) + randomKey := make([]byte, bitLength(hkgp.Length.Int64).asByteLength()) if _, err := rand.Read(randomKey); err != nil { // 4. return nil, NewError(OperationError, "failed to generate random key; reason: "+err.Error()) @@ -285,28 +288,20 @@ func (hip *HMACImportParams) ImportKey( } // 5. 6. - length := int64(len(keyData) * 8) + length := byteLength(len(keyData)).asBitLength() if length == 0 { return nil, NewError(DataError, "key length cannot be 0") } // 7. - if hip.Length.Valid { - if hip.Length.Int64 > length { - return nil, NewError(DataError, "key length cannot be greater than the length of the imported data") - } - - if hip.Length.Int64 < length { - return nil, NewError(DataError, "key length cannot be less than the length of the imported data") - } - - length = hip.Length.Int64 + if hip.Length.Valid && hip.Length.Int64 != int64(length) { + return nil, NewError(DataError, "key length cannot be different from the length of the imported data") } // 8. key := CryptoKey{ Type: SecretCryptoKeyType, - handle: keyData[:length/8], + handle: keyData[:length.asByteLength()], } // 9. @@ -316,7 +311,7 @@ func (hip *HMACImportParams) ImportKey( algorithm.Name = HMAC // 11. - algorithm.Length = length + algorithm.Length = int64(length) // 12. algorithm.Hash = hash diff --git a/webcrypto/types.go b/webcrypto/types.go index a2c3a97..73161d7 100644 --- a/webcrypto/types.go +++ b/webcrypto/types.go @@ -6,6 +6,22 @@ import ( "github.com/dop251/goja" ) +// bitLength is a type alias for the length of a bits collection. +type bitLength int + +// asByteLength returns the length of the bits collection in bytes. +func (b bitLength) asByteLength() byteLength { + return byteLength(b) / 8 +} + +// byteLength is a type alias for the length of a byte slice. +type byteLength int + +// asBitLength returns the length of the byte slice in bits. +func (b byteLength) asBitLength() bitLength { + return bitLength(b) * 8 +} + // ToBytes tries to return a byte slice from compatible types. func ToBytes(data interface{}) ([]byte, error) { switch dt := data.(type) {