diff --git a/contracts/contracts/Sapphire.sol b/contracts/contracts/Sapphire.sol index 1fd02b5d..5ee4aed9 100644 --- a/contracts/contracts/Sapphire.sol +++ b/contracts/contracts/Sapphire.sol @@ -29,6 +29,8 @@ library Sapphire { 0x0100000000000000000000000000000000000101; address internal constant SHA512 = 0x0100000000000000000000000000000000000102; + address internal constant SHA384 = + 0x0100000000000000000000000000000000000103; type Curve25519PublicKey is bytes32; type Curve25519SecretKey is bytes32; @@ -52,7 +54,9 @@ library Sapphire { // Sr25519 signature over the provided message. Sr25519, // Secp256r1 signature over the provided SHA-256 digest. - Secp256r1PrehashedSha256 + Secp256r1PrehashedSha256, + // Secp384r1 signature over the provided SHA-384 digest. + Secp384r1PrehashedSha384 } /** @@ -231,7 +235,7 @@ library Sapphire { * **not** vulnerable to length-extension attacks. * * @custom:standard https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf - * @custom:see @oasisprotocol/oasis-sdk :: precompile/sha512.rs :: call_sha512_256 + * @custom:see @oasisprotocol/oasis-sdk :: precompile/sha2.rs :: call_sha512_256 * @param input Bytes to hash * @return result 32 byte digest */ @@ -247,7 +251,7 @@ function sha512_256(bytes memory input) view returns (bytes32 result) { * Hash the input data with SHA-512 * * @custom:standard https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf - * @custom:see @oasisprotocol/oasis-sdk :: precompile/sha512.rs :: call_sha512 + * @custom:see @oasisprotocol/oasis-sdk :: precompile/sha2.rs :: call_sha512 * @param input Bytes to hash * @return output 64 byte digest */ @@ -258,3 +262,19 @@ function sha512(bytes memory input) view returns (bytes memory output) { require(success, "sha512"); } + +/** + * Hash the input data with SHA-384 + * + * @custom:standard https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf + * @custom:see @oasisprotocol/oasis-sdk :: precompile/sha2.rs :: call_sha384 + * @param input Bytes to hash + * @return output 48 byte digest + */ +function sha384(bytes memory input) view returns (bytes memory output) { + bool success; + + (success, output) = Sapphire.SHA384.staticcall(input); + + require(success, "sha384"); +} diff --git a/contracts/contracts/tests/HashTests.sol b/contracts/contracts/tests/HashTests.sol index 1145826a..a20e8e01 100644 --- a/contracts/contracts/tests/HashTests.sol +++ b/contracts/contracts/tests/HashTests.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; -import {sha512, sha512_256} from "../Sapphire.sol"; +import {sha512, sha512_256, sha384} from "../Sapphire.sol"; contract HashTests { function testSHA512(bytes memory data) @@ -13,6 +13,14 @@ contract HashTests { return sha512(data); } + function testSHA384(bytes memory data) + external + view + returns (bytes memory) + { + return sha384(data); + } + function testSHA512_256(bytes memory data) external view returns (bytes32) { return sha512_256(data); } diff --git a/contracts/test/hashes.ts b/contracts/test/hashes.ts index 29e8fe2f..b4d524eb 100644 --- a/contracts/test/hashes.ts +++ b/contracts/test/hashes.ts @@ -3,6 +3,13 @@ import { randomBytes, createHash } from 'crypto'; import { ethers } from 'hardhat'; import { HashTests } from '../typechain-types/contracts/tests/HashTests'; import { HashTests__factory } from '../typechain-types/factories/contracts/tests'; +import { PromiseOrValue } from '../typechain-types/common'; +import { BytesLike, CallOverrides } from 'ethers'; + +type HasherTestT = ( + data: PromiseOrValue, + overrides?: CallOverrides | undefined, +) => Promise; describe('Hashes', () => { let contract: HashTests; @@ -15,21 +22,24 @@ describe('Hashes', () => { await contract.deployed(); }); - it('SHA512/256', async () => { + async function testHashes(algname: string, method: HasherTestT) { for (let i = 0; i < 512; i += 64) { const data = randomBytes(i); - const hash = createHash('SHA512-256').update(data).digest('hex'); - const result = await contract.testSHA512_256(data); + const hash = createHash(algname).update(data).digest('hex'); + const result = await method(data); expect(result).eq('0x' + hash); } + } + + it('SHA512-256', async () => { + testHashes('SHA512-256', contract.testSHA512_256.bind(contract)); }); it('SHA512', async () => { - for (let i = 0; i < 512; i += 64) { - const data = randomBytes(i); - const hash = createHash('SHA512').update(data).digest('hex'); - const result = await contract.testSHA512(data); - expect(result).eq('0x' + hash); - } + testHashes('SHA512', contract.testSHA512.bind(contract)); + }); + + it('SHA384', async () => { + testHashes('SHA384', contract.testSHA384.bind(contract)); }); }); diff --git a/contracts/test/signing.ts b/contracts/test/signing.ts index 59658c0a..be2474bf 100644 --- a/contracts/test/signing.ts +++ b/contracts/test/signing.ts @@ -90,6 +90,7 @@ const VARYING_SIZED_BUFFERS = [ randomBytes(1), randomBytes(16), randomBytes(32), + randomBytes(48), randomBytes(64), ]; @@ -213,4 +214,19 @@ describe('Signing', function () { 0, ); }); + + it('Secp384r1 (Prehashed SHA384)', async () => { + // Try Secp384r1 (alg=8) + // 48 byte context, empty message + const sha384_kp = await se.testKeygen(8, randomBytes(48)); + await testSignThenVerify( + se, + 8, + sha384_kp, + randomBytes(48), + EMPTY_BUFFER, + 48, + 0, + ); + }); });