/// <summary> /// Updates the key pair one step. Should be called my MCT in order to safely advance key. /// </summary> /// <param name="keyPair"></param> /// <returns></returns> public async Task <HssKeyPair> UpdateKeyPairOneStepAsync(HssKeyPair keyPair) { var keyPairCopy = keyPair.GetDeepCopy(); int d = _lms.Length; while (keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q == (keyPairCopy.PrivateKey.PrivateKeys[d - 1].OTS_PRIV.Length / (_lms[d - 1].GetLmotsN() * _lms[d - 1].GetLmotsP() + 24)) - 1) { d--; if (d == 0) { keyPairCopy.Expired = true; return(keyPairCopy); } } var lowD = d; if (d < _lms.Length) { while (d < _lms.Length) { var newSeed = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1, 32)) .ConcatenateBits(new BitString(65534, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; var newI = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1, 32)) .ConcatenateBits(new BitString(65535, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; newI = newI.MSBSubstring(0, 128); _lms[d] = new Lms(_lmsTypes[d], _lmotsTypes[d], _entropyType, newSeed, newI); var newkeyPairCopy = await _lms[d].GenerateLmsKeyPairAsync(); keyPairCopy.PrivateKey.PrivateKeys[d] = newkeyPairCopy.PrivateKey; keyPairCopy.PrivateKey.PublicKeys[d] = newkeyPairCopy.PublicKey; keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q = (keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1) % (1 << _lms[d - 1].GetH()); keyPairCopy.PrivateKey.Signatures[d - 1] = _lms[d - 1].GenerateLmsSignature( keyPairCopy.PrivateKey.PublicKeys[d], keyPairCopy.PrivateKey.PrivateKeys[d - 1]); if (keyPairCopy.PrivateKey.Signatures[d - 1] == null) { keyPairCopy.Expired = true; return(keyPairCopy); } d++; } } else { // Update Q value keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q = (keyPairCopy.PrivateKey.PrivateKeys[d - 1].Q + 1) % (1 << _lms[d - 1].GetH()); } return(keyPairCopy); }
// returns null if keyPair is exhausted public async Task <HssSignatureResult> GenerateHssSignatureAsync(BitString msg, HssKeyPair keyPair, int advanced = 0) { // 1. If the message-signing key prv[L - 1] is exhausted, regenerate that key pair, together with any // parent key pairs that might be necessary. If the root key pair is exhausted, then the HSS key pair is // exhausted and MUST NOT generate any more signatures. keyPair = await UpdateKeyPairAsync(keyPair, advanced); if (keyPair.Expired) { return(new HssSignatureResult("Key expired.")); } // 2. Sign the message. var sig = _lms[_lms.Length - 1].GenerateLmsSignature(msg, keyPair.PrivateKey.PrivateKeys[_lms.Length - 1]); // 3. Create the list of signed public keys. var signedPubKey = new BitString(""); for (int i = 0; i < _lms.Length - 1; i++) { signedPubKey = signedPubKey.ConcatenateBits(keyPair.PrivateKey.Signatures[i]) .ConcatenateBits(keyPair.PrivateKey.PublicKeys[i + 1]); } // 4. Return u32str(L-1) || signed_pub_key[0] || ... || signed_pub_key[L - 2] || sig[L - 1] return(new HssSignatureResult(new BitString(_lms.Length - 1, 32).ConcatenateBits(signedPubKey).ConcatenateBits(sig))); }
/// <summary> /// Used in signature generation to sign after advancing the key some number of times. /// This function is unsafe to use if the keyPair has been advanced before. /// </summary> /// <param name="keyPair"></param> /// <param name="times"></param> /// <returns></returns> private async Task <HssKeyPair> UpdateKeyPairAsync(HssKeyPair keyPair, int times = 1) { if (times == 0) { return(keyPair); } keyPair = keyPair.GetDeepCopy(); var divisor = 1; for (int i = 1; i < _lms.Length; i++) { divisor *= (1 << _lms[i].GetH()); } // If update would cause the key to expire, then expire key if (((1 << _lms[0].GetH()) - keyPair.PrivateKey.PrivateKeys[0].Q) * divisor <= times) { keyPair.Expired = true; return(keyPair); } for (int d = 0; d < _lms.Length; d++) { // Update divisor for next step if (d != 0) { divisor /= (1 << _lms[d].GetH()); } var qStep = times / divisor; // If tree update is needed if (qStep + keyPair.PrivateKey.PrivateKeys[d].Q >= (1 << _lms[d].GetH())) { // If update would cause the key to expire, then expire key if (d == 0) { keyPair.Expired = true; return(keyPair); } var newSeed = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPair.PrivateKey.PrivateKeys[d - 1].Q, 32)) .ConcatenateBits(new BitString(65534, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; var newI = _sha256.HashMessage(_lms[d - 1].GetI() .ConcatenateBits(new BitString(keyPair.PrivateKey.PrivateKeys[d - 1].Q, 32)) .ConcatenateBits(new BitString(65535, 16)) .ConcatenateBits(new BitString("ff", 8)) .ConcatenateBits(_lms[d - 1].GetSeed())).Digest; newI = newI.MSBSubstring(0, 128); _lms[d] = new Lms(_lmsTypes[d], _lmotsTypes[d], _entropyType, newSeed, newI); var newKeyPair = await _lms[d].GenerateLmsKeyPairAsync(); keyPair.PrivateKey.PrivateKeys[d] = newKeyPair.PrivateKey; keyPair.PrivateKey.PublicKeys[d] = newKeyPair.PublicKey; keyPair.PrivateKey.Signatures[d - 1] = _lms[d - 1].GenerateLmsSignature( keyPair.PrivateKey.PublicKeys[d], keyPair.PrivateKey.PrivateKeys[d - 1]); } // Update Q value keyPair.PrivateKey.PrivateKeys[d].Q = (keyPair.PrivateKey.PrivateKeys[d].Q + qStep) % (1 << _lms[d].GetH()); } return(keyPair); }