diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 37f065fe..3ae72e6f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -14,7 +14,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        node-version: [12, 14, 16]
+        node-version: [16, 18, 20]
     steps:
       - uses: actions/checkout@v2
       - uses: actions/setup-node@v3
@@ -28,7 +28,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        node-version: [14, 16]
+        node-version: [16, 18, 20]
     steps:
       - uses: actions/checkout@v2
       - uses: actions/setup-node@v3
diff --git a/js/sign/README.md b/js/sign/README.md
index 89ff4054..0c0cb9b6 100644
--- a/js/sign/README.md
+++ b/js/sign/README.md
@@ -17,7 +17,7 @@ npm install wbn-sign
 
 ## Requirements
 
-This plugin requires Node v14.0.0+.
+This plugin requires Node v16.0.0+.
 
 ## Usage
 
@@ -179,6 +179,10 @@ environment variable named `WEB_BUNDLE_SIGNING_PASSPHRASE`.
 
 ## Release Notes
 
+### v0.1.3
+
+- Add support for ECDSA P-256 SHA-256 signatures
+
 ### v0.1.2
 
 - Add support for calculating the Web Bundle ID with the CLI tool.
diff --git a/js/sign/package-lock.json b/js/sign/package-lock.json
index 2acafc78..95b56630 100644
--- a/js/sign/package-lock.json
+++ b/js/sign/package-lock.json
@@ -19,7 +19,7 @@
         "wbn-sign": "bin/wbn-sign.js"
       },
       "devDependencies": {
-        "@types/node": "^14.0.0",
+        "@types/node": "^16.0.0",
         "esbuild": "^0.14.47",
         "jasmine": "^4.2.1",
         "mock-stdin": "^1.0.0",
@@ -27,7 +27,7 @@
         "typescript": "^4.7.3"
       },
       "engines": {
-        "node": ">= 14.0.0",
+        "node": ">= 16.0.0",
         "npm": ">= 8.0.0"
       }
     },
@@ -48,9 +48,9 @@
       }
     },
     "node_modules/@types/node": {
-      "version": "14.18.54",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.54.tgz",
-      "integrity": "sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw==",
+      "version": "16.18.96",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.96.tgz",
+      "integrity": "sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ==",
       "dev": true
     },
     "node_modules/balanced-match": {
@@ -626,9 +626,9 @@
       "optional": true
     },
     "@types/node": {
-      "version": "14.18.54",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.54.tgz",
-      "integrity": "sha512-uq7O52wvo2Lggsx1x21tKZgqkJpvwCseBBPtX/nKQfpVlEsLOb11zZ1CRsWUKvJF0+lzuA9jwvA7Pr2Wt7i3xw==",
+      "version": "16.18.96",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.96.tgz",
+      "integrity": "sha512-84iSqGXoO+Ha16j8pRZ/L90vDMKX04QTYMTfYeE1WrjWaZXuchBehGUZEpNgx7JnmlrIHdnABmpjrQjhCnNldQ==",
       "dev": true
     },
     "balanced-match": {
diff --git a/js/sign/package.json b/js/sign/package.json
index af404fc0..446f2ed4 100644
--- a/js/sign/package.json
+++ b/js/sign/package.json
@@ -1,6 +1,6 @@
 {
   "name": "wbn-sign",
-  "version": "0.1.2",
+  "version": "0.1.3",
   "description": "Signing tool to sign a web bundle with integrity block",
   "homepage": "https://github.com/WICG/webpackage/tree/main/js/sign",
   "main": "./lib/wbn-sign.cjs",
@@ -33,7 +33,8 @@
   ],
   "author": "Sonja Laurila <laurila@google.com> (https://github.com/sonkkeli)",
   "contributors": [
-    "Christian Flach <cmfcmf@google.com> (https://github.com/cmfcmf)"
+    "Christian Flach <cmfcmf@google.com> (https://github.com/cmfcmf)",
+    "Andrew Rayskiy <greengrape@google.com> (https://github.com/GrapeGreen)"
   ],
   "license": "W3C-20150513",
   "dependencies": {
@@ -43,7 +44,7 @@
     "read": "^2.0.0"
   },
   "devDependencies": {
-    "@types/node": "^14.0.0",
+    "@types/node": "^16.0.0",
     "esbuild": "^0.14.47",
     "jasmine": "^4.2.1",
     "mock-stdin": "^1.0.0",
@@ -51,7 +52,7 @@
     "typescript": "^4.7.3"
   },
   "engines": {
-    "node": ">= 14.0.0",
+    "node": ">= 16.0.0",
     "npm": ">= 8.0.0"
   },
   "prettier": {
diff --git a/js/sign/src/signers/integrity-block-signer.ts b/js/sign/src/signers/integrity-block-signer.ts
index 11013596..8a5d0d18 100644
--- a/js/sign/src/signers/integrity-block-signer.ts
+++ b/js/sign/src/signers/integrity-block-signer.ts
@@ -1,15 +1,15 @@
 import crypto, { KeyObject } from 'crypto';
 import * as cborg from 'cborg';
-import {
-  ED25519_PK_SIGNATURE_ATTRIBUTE_NAME,
-  INTEGRITY_BLOCK_MAGIC,
-  VERSION_B1,
-} from '../utils/constants.js';
+import { INTEGRITY_BLOCK_MAGIC, VERSION_B1 } from '../utils/constants.js';
 import { checkDeterministic } from '../cbor/deterministic.js';
-import { getRawPublicKey, checkIsValidEd25519Key } from '../utils/utils.js';
+import {
+  getRawPublicKey,
+  checkIsValidKey,
+  getPublicKeyAttributeName,
+} from '../utils/utils.js';
 import { ISigningStrategy } from './signing-strategy-interface.js';
 
-type SignatureAttributeKey = typeof ED25519_PK_SIGNATURE_ATTRIBUTE_NAME;
+type SignatureAttributeKey = string;
 type SignatureAttributes = { [SignatureAttributeKey: string]: Uint8Array };
 
 type IntegritySignature = {
@@ -29,10 +29,10 @@ export class IntegrityBlockSigner {
   }> {
     const integrityBlock = this.obtainIntegrityBlock().integrityBlock;
     const publicKey = await this.signingStrategy.getPublicKey();
-    checkIsValidEd25519Key('public', publicKey);
+    checkIsValidKey('public', publicKey);
 
     const newAttributes: SignatureAttributes = {
-      [ED25519_PK_SIGNATURE_ATTRIBUTE_NAME]: getRawPublicKey(publicKey),
+      [getPublicKeyAttributeName(publicKey)]: getRawPublicKey(publicKey),
     };
 
     const ibCbor = integrityBlock.toCBOR();
@@ -56,6 +56,7 @@ export class IntegrityBlockSigner {
 
     const signedIbCbor = integrityBlock.toCBOR();
     checkDeterministic(signedIbCbor);
+
     return {
       integrityBlock: signedIbCbor,
       signedWebBundle: new Uint8Array(
@@ -132,6 +133,7 @@ export class IntegrityBlockSigner {
     signature: Uint8Array,
     publicKey: KeyObject
   ): void {
+    // For ECDSA P-256 keys the algorithm is implicitly selected as SHA-256.
     const isVerified = crypto.verify(
       /*algorithm=*/ undefined,
       data,
diff --git a/js/sign/src/signers/node-crypto-signing-strategy.ts b/js/sign/src/signers/node-crypto-signing-strategy.ts
index b9011024..d3839579 100644
--- a/js/sign/src/signers/node-crypto-signing-strategy.ts
+++ b/js/sign/src/signers/node-crypto-signing-strategy.ts
@@ -1,15 +1,16 @@
 import crypto, { KeyObject } from 'crypto';
-import { checkIsValidEd25519Key } from '../utils/utils.js';
+import { checkIsValidKey } from '../utils/utils.js';
 import { ISigningStrategy } from './signing-strategy-interface.js';
 
 // Class to be used when signing with parsed `crypto.KeyObject` private key
 // provided directly in the constructor.
 export class NodeCryptoSigningStrategy implements ISigningStrategy {
   constructor(private readonly privateKey: KeyObject) {
-    checkIsValidEd25519Key('private', privateKey);
+    checkIsValidKey('private', privateKey);
   }
 
   async sign(data: Uint8Array): Promise<Uint8Array> {
+    // For ECDSA P-256 keys the algorithm is implicitly selected as SHA-256.
     return crypto.sign(/*algorithm=*/ undefined, data, this.privateKey);
   }
 
diff --git a/js/sign/src/utils/constants.ts b/js/sign/src/utils/constants.ts
index fc721bb6..15225e27 100644
--- a/js/sign/src/utils/constants.ts
+++ b/js/sign/src/utils/constants.ts
@@ -1,4 +1,15 @@
-export const ED25519_PK_SIGNATURE_ATTRIBUTE_NAME = 'ed25519PublicKey';
+export enum SignatureType {
+  Ed25519,
+  EcdsaP256SHA256,
+}
+
+export const PUBLIC_KEY_ATTRIBUTE_NAME_MAPPING = new Map<SignatureType, string>(
+  [
+    [SignatureType.Ed25519, 'ed25519PublicKey'],
+    [SignatureType.EcdsaP256SHA256, 'ecdsaP256SHA256PublicKey'],
+  ]
+);
+
 export const INTEGRITY_BLOCK_MAGIC = new Uint8Array([
   0xf0, 0x9f, 0x96, 0x8b, 0xf0, 0x9f, 0x93, 0xa6,
 ]); // 🖋📦
diff --git a/js/sign/src/utils/utils.ts b/js/sign/src/utils/utils.ts
index d71dcdaa..b62de759 100644
--- a/js/sign/src/utils/utils.ts
+++ b/js/sign/src/utils/utils.ts
@@ -1,5 +1,10 @@
 import crypto, { KeyObject } from 'crypto';
 import read from 'read';
+import assert from 'assert';
+import {
+  PUBLIC_KEY_ATTRIBUTE_NAME_MAPPING,
+  SignatureType,
+} from './constants.js';
 
 // A helper function that can be used to read the passphrase to decrypt a
 // password-decrypted private key.
@@ -31,15 +36,62 @@ export function parsePemKey(
   });
 }
 
-export function getRawPublicKey(publicKey: crypto.KeyObject) {
-  // Currently this is the only way for us to get the raw 32 bytes of the public key.
-  return new Uint8Array(
-    publicKey.export({ type: 'spki', format: 'der' }).slice(-32)
+function maybeGetSignatureType(key: crypto.KeyObject): SignatureType | null {
+  switch (key.asymmetricKeyType) {
+    case 'ed25519':
+      return SignatureType.Ed25519;
+    case 'ec':
+      if (key.asymmetricKeyDetails?.namedCurve === 'prime256v1') {
+        return SignatureType.EcdsaP256SHA256;
+      }
+      break;
+    default:
+      break;
+  }
+  return null;
+}
+
+export function isAsymmetricKeyTypeSupported(key: crypto.KeyObject): boolean {
+  return maybeGetSignatureType(key) !== null;
+}
+
+export function getSignatureType(key: crypto.KeyObject): SignatureType {
+  const signatureType = maybeGetSignatureType(key);
+  assert(
+    signatureType !== null,
+    'Expected either "Ed25519" or "ECDSA P-256" key.'
   );
+  return signatureType;
+}
+
+export function getPublicKeyAttributeName(key: crypto.KeyObject) {
+  return PUBLIC_KEY_ATTRIBUTE_NAME_MAPPING.get(getSignatureType(key))!;
 }
 
-// Throws an error if the key is not a valid Ed25519 key of the specified type.
-export function checkIsValidEd25519Key(
+export function getRawPublicKey(publicKey: crypto.KeyObject) {
+  const exportedKey = publicKey.export({ type: 'spki', format: 'der' });
+  switch (getSignatureType(publicKey)) {
+    case SignatureType.Ed25519:
+      // Currently this is the only way for us to get the raw 32 bytes of the public key.
+      return new Uint8Array(exportedKey.subarray(-32));
+    case SignatureType.EcdsaP256SHA256: {
+      // The last 65 bytes are the raw bytes of the ECDSA P-256 public key.
+      // For the purposes of signing, we'd like to convert it to its compressed form that takes only 33 bytes.
+      const uncompressedKey = exportedKey.subarray(-65);
+      const compressedKey = crypto.ECDH.convertKey(
+        uncompressedKey,
+        'prime256v1',
+        /*inputEncoding=*/ undefined,
+        /*outputEncoding=*/ undefined,
+        'compressed'
+      ) as Buffer;
+      return new Uint8Array(compressedKey);
+    }
+  }
+}
+
+// Throws an error if the key is not a valid Ed25519 or ECDSA P-256 key of the specified type.
+export function checkIsValidKey(
   expectedKeyType: crypto.KeyObjectType,
   key: KeyObject
 ) {
@@ -49,9 +101,7 @@ export function checkIsValidEd25519Key(
     );
   }
 
-  if (key.asymmetricKeyType !== 'ed25519') {
-    throw new Error(
-      `Expected asymmetric key type to be "ed25519", but it was "${key.asymmetricKeyType}".`
-    );
+  if (!isAsymmetricKeyTypeSupported(key)) {
+    throw new Error(`Expected either "Ed25519" or "ECDSA P-256" key.`);
   }
 }
diff --git a/js/sign/src/web-bundle-id.ts b/js/sign/src/web-bundle-id.ts
index efa8b30f..16abeb45 100644
--- a/js/sign/src/web-bundle-id.ts
+++ b/js/sign/src/web-bundle-id.ts
@@ -1,33 +1,44 @@
 import crypto, { KeyObject } from 'crypto';
 import base32Encode from 'base32-encode';
-import { getRawPublicKey } from './utils/utils.js';
+import {
+  getRawPublicKey,
+  isAsymmetricKeyTypeSupported,
+  getSignatureType,
+} from './utils/utils.js';
+import { SignatureType } from './utils/constants.js';
 
 // Web Bundle ID is a base32-encoded (without padding) ed25519 public key
 // transformed to lowercase. More information:
 // https://github.com/WICG/isolated-web-apps/blob/main/Scheme.md#signed-web-bundle-ids
 export class WebBundleId {
   // https://github.com/WICG/isolated-web-apps/blob/main/Scheme.md#suffix
-  private readonly appIdSuffix = [0x00, 0x01, 0x02];
+  private readonly TYPE_SUFFIX_MAPPING = new Map<SignatureType, number[]>([
+    [SignatureType.Ed25519, [0x00, 0x01, 0x02]],
+    [SignatureType.EcdsaP256SHA256, [0x00, 0x02, 0x02]],
+  ]);
   private readonly scheme = 'isolated-app://';
   private readonly key: KeyObject;
+  private readonly typeSuffix: number[];
 
-  constructor(ed25519key: KeyObject) {
-    if (ed25519key.asymmetricKeyType !== 'ed25519') {
+  constructor(key: KeyObject) {
+    if (!isAsymmetricKeyTypeSupported(key)) {
       throw new Error(
-        `WebBundleId: Only ed25519 keys are currently supported. Your key's type is ${ed25519key.asymmetricKeyType}.`
+        `WebBundleId: Only Ed25519 and ECDSA P-256 keys are currently supported.`
       );
     }
 
-    if (ed25519key.type === 'private') {
-      this.key = crypto.createPublicKey(ed25519key);
+    if (key.type === 'private') {
+      this.key = crypto.createPublicKey(key);
     } else {
-      this.key = ed25519key;
+      this.key = key;
     }
+
+    this.typeSuffix = this.TYPE_SUFFIX_MAPPING.get(getSignatureType(this.key))!;
   }
 
   serialize() {
     return base32Encode(
-      new Uint8Array([...getRawPublicKey(this.key), ...this.appIdSuffix]),
+      new Uint8Array([...getRawPublicKey(this.key), ...this.typeSuffix]),
       'RFC4648',
       { padding: false }
     ).toLowerCase();
diff --git a/js/sign/telnet.swbn b/js/sign/telnet.swbn
new file mode 100644
index 00000000..fc66404a
Binary files /dev/null and b/js/sign/telnet.swbn differ
diff --git a/js/sign/tests/integrity-block-signer_test.js b/js/sign/tests/integrity-block-signer_test.js
index 9366dca7..d55918b5 100644
--- a/js/sign/tests/integrity-block-signer_test.js
+++ b/js/sign/tests/integrity-block-signer_test.js
@@ -1,5 +1,6 @@
 import * as wbnSign from '../lib/wbn-sign.js';
 import * as constants from '../lib/utils/constants.js';
+import * as utils from '../lib/utils/utils.js';
 import * as fs from 'fs';
 import * as path from 'path';
 import * as crypto from 'crypto';
@@ -10,22 +11,46 @@ const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
 const TEST_WEB_BUNDLE_HASH =
   '95f8713d382ffefb8f1e4f464e39a2bf18280c8b26434d2fcfc08d7d710c8919ace5a652e25e66f9292cda424f20e4b53bf613bf9488140272f56a455393f7e6';
 const EMPTY_INTEGRITY_BLOCK_HEX = '8348f09f968bf09f93a6443162000080';
-const TEST_PRIVATE_KEY =
-  '-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIB8nP5PpWU7HiILHSfh5PYzb5GAcIfHZ+bw6tcd/LZXh\n-----END PRIVATE KEY-----';
-const TEST_WEB_BUNDLE_ID =
+const TEST_ED25519_PRIVATE_KEY = `-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIB8nP5PpWU7HiILHSfh5PYzb5GAcIfHZ+bw6tcd/LZXh
+-----END PRIVATE KEY-----`;
+const TEST_ECDSA_P256_PRIVATE_KEY = `
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIG6HAXvoG+dOP20rbyPuGC21od4DAZCKBkPy/1902xPnoAoGCCqGSM49
+AwEHoUQDQgAEHIIHO9B+7XJoXTXf3aTWC7aoK1PW4Db5Z8gSGXIkHlLrucUI4lyx
+DttYYhi36vrg5nR6zrfdhe7+8F1MoTvLuw==
+-----END EC PRIVATE KEY-----`;
+const TEST_ED25519_WEB_BUNDLE_ID =
   '4tkrnsmftl4ggvvdkfth3piainqragus2qbhf7rlz2a3wo3rh4wqaaic';
+const TEST_ECDSA_P256_WEB_BUNDLE_ID =
+  'amoiebz32b7o24tilu257xne2yf3nkblkploanxzm7ebeglseqpfeaacai';
+
 const IWA_SCHEME = 'isolated-app://';
 
 describe('Web Bundle ID', () => {
-  const privateKey = wbnSign.parsePemKey(TEST_PRIVATE_KEY);
-  const testKeys = [privateKey, crypto.createPublicKey(privateKey)];
+  const ed25519PrivateKey = wbnSign.parsePemKey(TEST_ED25519_PRIVATE_KEY);
+  const ecdsaP256PrivateKey = wbnSign.parsePemKey(TEST_ECDSA_P256_PRIVATE_KEY);
+  const testKeys = [
+    ed25519PrivateKey,
+    crypto.createPublicKey(ed25519PrivateKey),
+    ecdsaP256PrivateKey,
+    crypto.createPublicKey(ecdsaP256PrivateKey),
+  ];
 
   testKeys.forEach((key, index) => {
     it(`calculates the ID and isolated web app origin correctly with key #${index}.`, () => {
-      expect(TEST_WEB_BUNDLE_ID).toEqual(
+      const expectedWebBundleId = (() => {
+        switch (utils.getSignatureType(key)) {
+          case constants.SignatureType.Ed25519:
+            return TEST_ED25519_WEB_BUNDLE_ID;
+          case constants.SignatureType.EcdsaP256SHA256:
+            return TEST_ECDSA_P256_WEB_BUNDLE_ID;
+        }
+      })();
+      expect(expectedWebBundleId).toEqual(
         new wbnSign.WebBundleId(key).serialize()
       );
-      expect(`${IWA_SCHEME}${TEST_WEB_BUNDLE_ID}/`).toEqual(
+      expect(`${IWA_SCHEME}${expectedWebBundleId}/`).toEqual(
         new wbnSign.WebBundleId(key).serializeWithIsolatedWebAppOrigin()
       );
     });
@@ -43,11 +68,23 @@ describe('Integrity Block Signer', () => {
     return signer;
   }
 
-  it('accepts only ed25519 type of key.', () => {
-    const keypair = crypto.generateKeyPairSync('ed25519');
-    expect(() =>
-      initSignerWithTestWebBundleAndKeys(keypair.privateKey)
-    ).not.toThrowError();
+  function createTestSuffix(publicKey) {
+    return constants.SignatureType[utils.getSignatureType(publicKey)];
+  }
+
+  it('accepts only selected key types.', () => {
+    for (const validKey of [
+      { keyType: 'ed25519' },
+      { keyType: 'ec', options: { namedCurve: 'prime256v1' } },
+    ]) {
+      const keypairValid = crypto.generateKeyPairSync(
+        validKey.keyType,
+        validKey.options
+      );
+      expect(() =>
+        initSignerWithTestWebBundleAndKeys(keypairValid.privateKey)
+      ).not.toThrowError();
+    }
 
     for (const invalidKey of [
       { keyType: 'rsa', options: { modulusLength: 2048 } },
@@ -91,63 +128,93 @@ describe('Integrity Block Signer', () => {
     );
   });
 
-  it('generates the dataToBeSigned correctly.', () => {
-    const keypair = crypto.generateKeyPairSync('ed25519');
-    const signer = initSignerWithTestWebBundleAndKeys(keypair.privateKey);
-    const rawPubKey = wbnSign.getRawPublicKey(keypair.publicKey);
-    const dataToBeSigned = signer.generateDataToBeSigned(
-      signer.calcWebBundleHash(),
-      new wbnSign.IntegrityBlock().toCBOR(),
-      cborg.encode({
-        [constants.ED25519_PK_SIGNATURE_ATTRIBUTE_NAME]: rawPubKey,
-      })
-    );
+  [
+    crypto.generateKeyPairSync('ed25519'),
+    crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' }),
+  ].forEach((keypair) => {
+    it(`generates the dataToBeSigned correctly with ${createTestSuffix(
+      keypair.publicKey
+    )}.`, () => {
+      const signer = initSignerWithTestWebBundleAndKeys(keypair.privateKey);
+      const rawPubKey = wbnSign.getRawPublicKey(keypair.publicKey);
+
+      const dataToBeSigned = signer.generateDataToBeSigned(
+        signer.calcWebBundleHash(),
+        new wbnSign.IntegrityBlock().toCBOR(),
+        cborg.encode({
+          [utils.getPublicKeyAttributeName(keypair.publicKey)]: rawPubKey,
+        })
+      );
 
-    const hexHashString =
-      /*64*/ '0000000000000040' +
-      TEST_WEB_BUNDLE_HASH +
-      /*16*/ '0000000000000010' +
-      EMPTY_INTEGRITY_BLOCK_HEX +
-      /*52*/ '0000000000000034' +
-      'a170656432353531395075626c69634b65795820' +
-      Buffer.from(rawPubKey).toString('hex');
-
-    expect(dataToBeSigned).toEqual(
-      Uint8Array.from(Buffer.from(hexHashString, 'hex'))
-    );
+      const attributesCborHex = (() => {
+        switch (utils.getSignatureType(keypair.publicKey)) {
+          case constants.SignatureType.Ed25519:
+            return (
+              /*52*/ '0000000000000034' +
+              'a170656432353531395075626c69634b65795820' +
+              Buffer.from(rawPubKey).toString('hex')
+            );
+          case constants.SignatureType.EcdsaP256SHA256:
+            return (
+              /*62*/ '000000000000003e' +
+              'a178186563647361503235365348413235365075626c69634b65795821' +
+              Buffer.from(rawPubKey).toString('hex')
+            );
+        }
+      })();
+
+      const hexHashString =
+        /*64*/ '0000000000000040' +
+        TEST_WEB_BUNDLE_HASH +
+        /*16*/ '0000000000000010' +
+        EMPTY_INTEGRITY_BLOCK_HEX +
+        attributesCborHex;
+
+      expect(dataToBeSigned).toEqual(
+        Uint8Array.from(Buffer.from(hexHashString, 'hex'))
+      );
+    });
   });
 
-  it('generates a valid signature.', async () => {
-    const keypair = crypto.generateKeyPairSync('ed25519');
-    const signer = initSignerWithTestWebBundleAndKeys(keypair.privateKey);
-    const rawPubKey = wbnSign.getRawPublicKey(keypair.publicKey);
-    const sigAttr = {
-      [constants.ED25519_PK_SIGNATURE_ATTRIBUTE_NAME]: rawPubKey,
-    };
-    const dataToBeSigned = signer.generateDataToBeSigned(
-      signer.calcWebBundleHash(),
-      new wbnSign.IntegrityBlock().toCBOR(),
-      cborg.encode(sigAttr)
-    );
+  [
+    crypto.generateKeyPairSync('ed25519'),
+    crypto.generateKeyPairSync('ec', { namedCurve: 'prime256v1' }),
+  ].forEach((keypair) => {
+    it(`generates a valid signature with ${createTestSuffix(
+      keypair.publicKey
+    )}.`, async () => {
+      const signer = initSignerWithTestWebBundleAndKeys(keypair.privateKey);
+      const rawPubKey = wbnSign.getRawPublicKey(keypair.publicKey);
+      const sigAttr = {
+        [utils.getPublicKeyAttributeName(keypair.publicKey)]: rawPubKey,
+      };
+      const dataToBeSigned = signer.generateDataToBeSigned(
+        signer.calcWebBundleHash(),
+        new wbnSign.IntegrityBlock().toCBOR(),
+        cborg.encode(sigAttr)
+      );
 
-    const ib = cborg.decode((await signer.sign()).integrityBlock);
-    expect(ib.length).toEqual(3);
-
-    const [magic, version, signatureStack] = ib;
-    expect(magic).toEqual(constants.INTEGRITY_BLOCK_MAGIC);
-    expect(version).toEqual(constants.VERSION_B1);
-    expect(signatureStack.length).toEqual(1);
-    expect(signatureStack[0].length).toEqual(2);
-
-    const [signatureAttributes, signature] = signatureStack[0];
-    expect(signatureAttributes).toEqual(sigAttr);
-    expect(
-      crypto.verify(
-        /*algorithm=*/ undefined,
-        dataToBeSigned,
-        keypair.publicKey,
-        signature
-      )
-    ).toBeTruthy();
+      const ib = cborg.decode((await signer.sign()).integrityBlock);
+      expect(ib.length).toEqual(3);
+
+      const [magic, version, signatureStack] = ib;
+      expect(magic).toEqual(constants.INTEGRITY_BLOCK_MAGIC);
+      expect(version).toEqual(constants.VERSION_B1);
+      expect(signatureStack.length).toEqual(1);
+      expect(signatureStack[0].length).toEqual(2);
+
+      const [signatureAttributes, signature] = signatureStack[0];
+      expect(signatureAttributes).toEqual(sigAttr);
+
+      // For ECDSA P-256 keys the algorithm is implicitly selected as SHA-256.
+      expect(
+        crypto.verify(
+          /*algorithm=*/ undefined,
+          dataToBeSigned,
+          keypair.publicKey,
+          signature
+        )
+      ).toBeTruthy();
+    });
   });
 });